Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/src/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 18.9.2025 mit Größe 107 kB image not shown  

Quelle  intrprtr.c

  Sprache: C
 

/****************************************************************************
**
**  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 immediate interpreter package.
**
**  The immediate interpreter package  is  the part  of the interpreter  that
**  interprets code immediately (while it is read).  Its functions are called
**  from the reader.  When it encounters  constructs that it cannot interpret
**  immediately, it switches into coding mode, and  delegates the work to the
**  coder.
*/


#include "intrprtr.h"

#include "ariths.h"
#include "bool.h"
#include "calls.h"
#include "code.h"
#include "error.h"
#include "funcs.h"
#include "gapstate.h"
#include "gvars.h"
#include "hookintrprtr.h"
#include "info.h"
#include "integer.h"
#include "io.h"
#include "lists.h"
#include "modules.h"
#include "opers.h"
#include "permutat.h"
#include "plist.h"
#include "precord.h"
#include "range.h"
#include "records.h"
#include "stringobj.h"
#include "trycatch.h"
#include "vars.h"

#ifdef HPCGAP
#include "hpc/aobjects.h"
#include "hpc/guards.h"
#endif

// INTERPRETER_PROFILE_HOOK deals with profiling of immediately executed
// code.
// If intr->coding is true, profiling is handled by the AST
// generation and execution. Otherwise, we always mark the line as
// read, and mark as executed if intr->returning and intr->ignoring
// are both false.
//
// IgnoreLevel gives the highest value of IntrIgnoring which means this
// statement is NOT ignored (this is usually, but not always, 0)
static void INTERPRETER_PROFILE_HOOK(IntrState * intr, int ignoreLevel)
{
    if (!intr->coding) {
        InterpreterHook(
            intr->gapnameid, intr->startLine,
            intr->returning != STATUS_END || (intr->ignoring > ignoreLevel));
    }
    intr->startLine = 0;
}


// Put the profiling hook into SKIP_IF_RETURNING, as this is run in
// (nearly) every part of the interpreter, avoid lots of extra code.
#define SKIP_IF_RETURNING()                                                  \
    INTERPRETER_PROFILE_HOOK(intr, 0);                                       \
    SKIP_IF_RETURNING_NO_PROFILE_HOOK();

// Need to
#define SKIP_IF_RETURNING_NO_PROFILE_HOOK()                                  \
    if (intr->returning != STATUS_END) {                                     \
        return;                                                              \
    }

#define SKIP_IF_IGNORING()                                                   \
    if (intr->ignoring > 0) {                                                \
        return;                                                              \
    }


/****************************************************************************
**
*F  PushObj(<val>)  . . . . . . . . . . . . . . . . push value onto the stack
*F  PushVoidObj() . . . . . . . . . . . . . .  push void value onto the stack
*F  PopObj()  . . . . . . . . . . . . . . . . . . .  pop value from the stack
*F  PopVoidObj()  . . . . . . . . . . . . . . . . .  pop value from the stack
**
**  'intr->StackObj' is the stack of values.
**
**  'PushObj' pushes the value <val>  onto the values stack.   It is an error
**  to push the void value.  The stack is automatically resized if necessary.
**
**  'PushVoidObj' pushes the void value onto the values stack.  This value is
**  the value of if-statements and loops and procedure calls.
**
**  'PopObj' returns the top element from  the values stack  and pops it.  It
**  is an error if the stack is empty or if the top element is void.
**
**  'PopVoidObj' returns the  top element from the values  stack and pops it.
**  It is an error if the stack is empty but not if the top element is void.
*/


static void PushObj(IntrState * intr, Obj val)
{
    GAP_ASSERT(val != 0);
    PushPlist(intr->StackObj, val);
}

/* Special marker value to denote that a function returned no value, so we
 * can produce a useful error message. This value only ever appears on the
 * stack, and should never be visible outside the Push and Pop methods below
 *
 * The only place other than these methods which access the stack is
 * the permutation reader, but it only directly accesses values it wrote,
 * so it will not see this magic value. */

static Obj VoidReturnMarker;

static void PushFunctionVoidReturn(IntrState * intr)
{
    PushPlist(intr->StackObj, VoidReturnMarker);
}

static void PushVoidObj(IntrState * intr)
{
    PushPlist(intr->StackObj, 0);
}

static Obj PopObj(IntrState * intr)
{
    Obj val = PopPlist(intr->StackObj);

    if (val == VoidReturnMarker) {
        ErrorQuit("Function call: <func> must return a value", 0, 0);
    }

    // return the popped value (which must be non-void)
    GAP_ASSERT(val != 0);
    return val;
}

static Obj PopVoidObj(IntrState * intr)
{
    Obj val = PopPlist(intr->StackObj);

    // Treat a function which returned no value the same as 'void'
    if (val == VoidReturnMarker) {
        val = 0;
    }

    // return the popped value (which may be void)
    return val;
}


static void StartFakeFuncExpr(IntrState * intr, Obj stackNams)
{
    GAP_ASSERT(intr->coding == 0);

    // switch to coding mode now
    CodeBegin(intr->cs);

    // code a function expression (with no arguments and locals)
    Obj nams = NEW_PLIST(T_PLIST, 0);

    // If we are in the break loop, then a local variable context may well
    // exist, and we have to create an empty local variable names list to
    // match the function expression that we are creating.
    //
    // Without this, access to variables defined in the existing local
    // variable context will be coded as LVAR accesses; but when we then
    // execute this code, they will not actually be available in the current
    // context, but rather one level up, i.e., they really should have been
    // coded as HVARs.
    //
    // If we are not in a break loop, then this would be a waste of time and
    // effort
    if (LEN_PLIST(stackNams) > 0) {
        PushPlist(stackNams, nams);
    }

    CodeFuncExprBegin(intr->cs, 0, 0, nams, intr->gapnameid, 0);
}


static void FinishAndCallFakeFuncExpr(IntrState * intr, Obj stackNams)
{
    GAP_ASSERT(intr->coding == 0);

    // code a function expression (with one statement in the body)
    CodeFuncExprEnd(intr->cs, 1, TRUE, 0);

    // switch back to immediate mode and get the function
    Obj func = CodeEnd(intr->cs, 0);

    // If we are in a break loop, then we will have created a "dummy" local
    // variable names list to get the counts right. Remove it.
    const UInt len = LEN_PLIST(stackNams);
    if (len > 0)
        PopPlist(stackNams);

    // call the function
    CALL_0ARGS(func);

    // push void
    PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrBegin(<intr>) . . . . . . . . . . . . . . . . .  start an interpreter
*F  IntrEnd(<intr>,<error>,<result>)  . . . . . . . . . . stop an interpreter
**
**  'IntrBegin' starts a new interpreter.
**
**  'IntrEnd' stops the given interpreter.
**
**  If <error>  is non-zero a  syntax error was found by  the reader, and the
**  interpreter only clears up the mess.
**
**  If 'IntrEnd' returns 'STATUS_END', then no return-statement or
**  quit-statement was interpreted. If 'IntrEnd' returns 'STATUS_RETURN',
**  then a return-statement was interpreted. If a value was returned, and the
**  <result> is non-zero, then the returned value is assigned to the address
**  <result> points at. If 'IntrEnd' returns 'STATUS_QUIT', then a
**  quit-statement was interpreted. If 'IntrEnd' returns 'STATUS_QQUIT', then
**  a QUIT-statement was interpreted.
*/

void IntrBegin(IntrState * intr)
{
    // allocate a new values stack
    intr->StackObj = NEW_PLIST(T_PLIST, 64);

    // must be in immediate (non-ignoring, non-coding) mode
    GAP_ASSERT(intr->ignoring == 0);
    GAP_ASSERT(intr->coding == 0);

    // no return-statement was yet interpreted
    intr->returning = STATUS_END;
}

ExecStatus IntrEnd(IntrState * intr, BOOL error, Obj * result)
{
    // if everything went fine
    if ( ! error ) {

        // must be back in immediate (non-ignoring, non-coding) mode
        GAP_ASSERT(intr->ignoring == 0);
        GAP_ASSERT(intr->coding == 0);

        // and the stack must contain the result value (which may be void)
        GAP_ASSERT(LEN_PLIST(intr->StackObj) == 1);
        if (result)
            *result = PopVoidObj(intr);

        return intr->returning;
    }

    // otherwise clean up the mess
    else {

        // clean up the coder too
        if (intr->coding > 0) {
            CodeEnd(intr->cs, 1);
        }

        // dummy result value (probably ignored)
        if (result)
            *result = 0;

        // indicate that we had an error
        return STATUS_ERROR;
    }
}


/****************************************************************************
**
*F  IntrAbortCoding() . . . . . . . . . . . . . . . . . . . . .  abort coding
**
**  'IntrAbortCoding' aborts coding, if it is active.
*/

void IntrAbortCoding(IntrState * intr)
{
    if (intr->coding) {
        CodeEnd(intr->cs, 1);
        intr->coding--;
    }
}


/****************************************************************************
**
*F  IntrFuncCallBegin() . . . . . . . . . . .  interpret function call, begin
*F  IntrFuncCallEnd(<funccall>,<options>, <nr>)  interpret function call, end
**
**  'IntrFuncCallBegin' is an action  to  interpret a  function call.  It  is
**  called by  the reader  when  it  encounters  the parenthesis  '(',  i.e.,
**  *after* the function expression is read.
**
**  'IntrFuncCallEnd'  is an  action to  interpret  a  function call.   It is
**  called by    the reader when it encounters     the parenthesis ')', i.e.,
**  *after* the argument expressions are read.  <funccall>  is 1 if this is a
**  function call, and 0 if this is a procedure call.  <nr>  is the number of
**  arguments. <options> is 1 if options were present after the ':' in which
**  case the options have been read already.
*/

void IntrFuncCallBegin(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeFuncCallBegin(intr->cs);
        return;
    }
}

static Obj PushOptions;
static Obj PopOptions;

void IntrFuncCallEnd(IntrState * intr, UInt funccall, UInt options, UInt nr)
{
    Obj                 func;           // function
    Obj                 a1;             // first argument
    Obj                 a2;             // second argument
    Obj                 a3;             // third argument
    Obj                 a4;             // fourth argument
    Obj                 a5;             // fifth  argument
    Obj                 a6;             // sixth  argument
    Obj                 args;           // argument list
    Obj                 argi;           // <i>-th argument
    Obj                 val;            // return value of function
    Obj                 opts;           // record of options
    UInt                i;              // loop variable

    // ignore or code
    SKIP_IF_RETURNING_NO_PROFILE_HOOK();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeFuncCallEnd(intr->cs, funccall, options, nr);
        return;
    }


    if (options) {
        opts = PopObj(intr);
        CALL_1ARGS(PushOptions, opts);
    }

    // get the arguments from the stack
    a1 = a2 = a3 = a4 = a5 = a6 = args = 0;
    if ( nr <= 6 ) {
        if ( 6 <= nr ) { a6 = PopObj(intr); }
        if ( 5 <= nr ) { a5 = PopObj(intr); }
        if ( 4 <= nr ) { a4 = PopObj(intr); }
        if ( 3 <= nr ) { a3 = PopObj(intr); }
        if ( 2 <= nr ) { a2 = PopObj(intr); }
        if ( 1 <= nr ) { a1 = PopObj(intr); }
    } else {
        args = NEW_PLIST( T_PLIST, nr );
        SET_LEN_PLIST( args, nr );
        for ( i = nr; 1 <= i; i-- ) {
            argi = PopObj(intr);
            SET_ELM_PLIST( args, i, argi );
        }
    }

    // get and check the function from the stack
    func = PopObj(intr);
    if ( TNUM_OBJ(func) != T_FUNCTION ) {
      if ( nr <= 6 ) {
        args = NEW_PLIST( T_PLIST_DENSE, nr );
        SET_LEN_PLIST( args, nr );
        switch(nr) {
        case 6: SET_ELM_PLIST(args,6,a6);
        case 5: SET_ELM_PLIST(args,5,a5);
        case 4: SET_ELM_PLIST(args,4,a4);
        case 3: SET_ELM_PLIST(args,3,a3);
        case 2: SET_ELM_PLIST(args,2,a2);
        case 1: SET_ELM_PLIST(args,1,a1);
        }
      }
      val = DoOperation2Args(CallFuncListOper, func, args);
    } else {
      // call the function
      if      ( 0 == nr ) { val = CALL_0ARGS( func ); }
      else if ( 1 == nr ) { val = CALL_1ARGS( func, a1 ); }
      else if ( 2 == nr ) { val = CALL_2ARGS( func, a1, a2 ); }
      else if ( 3 == nr ) { val = CALL_3ARGS( func, a1, a2, a3 ); }
      else if ( 4 == nr ) { val = CALL_4ARGS( func, a1, a2, a3, a4 ); }
      else if ( 5 == nr ) { val = CALL_5ARGS( func, a1, a2, a3, a4, a5 ); }
      else if ( 6 == nr ) { val = CALL_6ARGS( func, a1, a2, a3, a4, a5, a6 ); }
      else                { val = CALL_XARGS( func, args ); }

      if (STATE(UserHasQuit) || STATE(UserHasQUIT)) {
        // the procedure 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 procedure was called, and now we are returning from that
        GAP_THROW();
      }
    }

    if (options)
      CALL_0ARGS(PopOptions);

    // push the value onto the stack
    if ( val == 0 )
        PushFunctionVoidReturn(intr);
    else
        PushObj(intr, val);
}


/****************************************************************************
**
*F  IntrFuncExprBegin(<narg>,<nloc>,<nams>) .  interpret function expr, begin
*F  IntrFuncExprEnd(<nr>) . . . . . . . . . . .  interpret function expr, end
**
**  'IntrFuncExprBegin' is an action to interpret  a function expression.  It
**  is  called when   the reader  encounters  the  beginning  of  a  function
**  expression.  <narg> is the number of  arguments (-1 if the function takes
**  a variable number of arguments),  <nloc> is the  number of locals, <nams>
**  is a list of local variable names.
**
**  'IntrFuncExprEnd' is an action to interpret a function expression.  It is
**  called when the reader encounters the end  of a function expression. <nr>
**  is the number of statements in the body of the function.
*/

void IntrFuncExprBegin(
    IntrState * intr, Int narg, Int nloc, Obj nams, Int startLine)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    if (intr->coding == 0) {
        CodeBegin(intr->cs);
    }
    intr->coding++;

    // code a function expression
    CodeFuncExprBegin(intr->cs, narg, nloc, nams, intr->gapnameid, startLine);
}

#ifdef HPCGAP
void IntrFuncExprSetLocks(IntrState * intr, Obj locks)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);

    CodeFuncExprSetLocks(intr->cs, locks);
}
#endif

void IntrFuncExprEnd(IntrState * intr, UInt nr, Int endLine)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);

    intr->coding--;
    CodeFuncExprEnd(intr->cs, nr, TRUE, endLine);

    if (intr->coding == 0) {
        // switch back to immediate mode and get the function
        Obj func = CodeEnd(intr->cs, 0);

        // push the function
        PushObj(intr, func);
    }
}


/****************************************************************************
**
*F  IntrIfBegin() . . . . . . . .  interpret if-statement, begin of statement
*F  IntrIfElif()  . . . . . . .  interpret if-statement, begin of elif-branch
*F  IntrIfElse()  . . . . . . .  interpret if-statement, begin of else-branch
*F  IntrIfBeginBody() . . . . . . . . . interpret if-statement, begin of body
*F  IntrIfEndBody(<nr>) . . . . . . . . . interpret if-statement, end of body
*F  IntrIfEnd(<nr>) . . . . . . . .  interpret if-statement, end of statement
**
**  'IntrIfBegin' is an  action to interpret  an if-statement.   It is called
**  when the reader encounters the   'if',  i.e., *before* the condition   is
**  read.
**
**  'IntrIfElif' is  an action to   interpret an if-statement.  It is  called
**  when the  reader encounters an  'elif', i.e.,  *before* the condition  is
**  read.
**
**  'IntrIfElse' is  an  action to interpret an   if-statement.  It is called
**  when the reader encounters an 'else'.
**
**  'IntrIfBeginBody'  is  an action to   interpret  an if-statement.  It  is
**  called when the reader encounters the beginning  of the statement body of
**  an 'if', 'elif', or 'else' branch, i.e., *after* the condition is read.
**
**  'IntrIfEndBody' is an action to interpret  an if-statement.  It is called
**  when the reader  encounters the end of  the  statements body of an  'if',
**  'elif', or 'else' branch.  <nr> is the number of statements in the body.
**
**  'IntrIfEnd' is an action to interpret an if-statement.  It is called when
**  the reader  encounters the end of the  statement.  <nr>  is the number of
**  'if', 'elif', or 'else' branches.
*/

void IntrIfBegin(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();

    // if IntrIgnoring is positive, increment it, as IntrIgnoring == 1 has a
    // special meaning when parsing if-statements -- it is used to skip
    // interpreting or coding branches of the if-statement which never will
    // be executed, either because a previous branch is always executed
    // (i.e., it has a 'true' condition), or else because the current branch
    // has a 'false' condition
    if (intr->ignoring > 0) {
        intr->ignoring++;
        return;
    }
    if (intr->coding > 0) {
        CodeIfBegin(intr->cs);
        return;
    }
}

void IntrIfElif(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIfElif(intr->cs);
        return;
    }
}

void IntrIfElse(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIfElse(intr->cs);
        return;
    }


    // push 'true' (to execute body of else-branch)
    PushObj(intr, True);
}

void IntrIfBeginBody(IntrState * intr)
{
    Obj                 cond;           // value of condition

    // ignore or code
    SKIP_IF_RETURNING();
    if (intr->ignoring > 0) {
        intr->ignoring++;
        return;
    }
    if (intr->coding > 0) {
        intr->ignoring = CodeIfBeginBody(intr->cs);
        return;
    }


    // get and check the condition
    cond = PopObj(intr);
    if ( cond != True && cond != False ) {
        RequireArgumentEx(0, cond, "<expr>""must be 'true' or 'false'");
    }

    // if the condition is 'false', ignore the body
    if ( cond == False ) {
        intr->ignoring = 1;
    }
}

Int IntrIfEndBody(IntrState * intr, UInt nr)
{
    UInt                i;              // loop variable

    // explicitly check interpreter hooks, as not using SKIP_IF_RETURNING
    INTERPRETER_PROFILE_HOOK(intr, 0);

    // ignore or code
    if (intr->returning != STATUS_END) {
        return 0;
    }
    if (intr->ignoring > 0) {
        intr->ignoring--;
        return 0;
    }
    if (intr->coding > 0) {
        intr->ignoring = CodeIfEndBody(intr->cs, nr);
        return 1;
    }

    // otherwise drop the values for the statements executed in the body
    for ( i = nr; 1 <= i; i-- ) {
        PopVoidObj(intr);
    }

    // one branch of the if-statement was executed, ignore the others
    intr->ignoring = 1;

    return 1;
}

void IntrIfEnd(IntrState * intr, UInt nr)
{
    // ignore or code
    INTERPRETER_PROFILE_HOOK(intr, 1);
    SKIP_IF_RETURNING_NO_PROFILE_HOOK();

    if (intr->ignoring > 1) {
        intr->ignoring--;
        return;
    }

    // if one branch was executed (ignoring the others), reset IntrIgnoring
    if (intr->ignoring == 1) {
        intr->ignoring = 0;
    }

    if (intr->coding > 0) {
        CodeIfEnd(intr->cs, nr);
        return;
    }

    PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrForBegin()  . . . . . . . interpret for-statement, begin of statement
*F  IntrForIn() . . . . . . . . . . . . .  interpret for-statement, 'in'-read
*F  IntrForBeginBody()  . . . . . . .  interpret for-statement, begin of body
*F  IntrForEndBody(<nr>)  . . . . . . .  interpret for-statement, end of body
*F  IntrForEnd()  . . . . . . . . . interpret for-statement, end of statement
**
**  'IntrForBegin' is  an action to interpret  a for-statement.  It is called
**  when the   reader encounters the  'for', i.e.,  *before*  the variable is
**  read.
**
**  'IntrForIn' is an action to interpret a for-statement.  It is called when
**  the  reader encounters the 'in', i.e.,  *after* the variable is read, but
**  *before* the list expression is read.
**
**  'IntrForBeginBody'  is  an action to interpret   a for-statement.   It is
**  called when  the reader encounters  the beginning  of the statement body,
**  i.e., *after* the list expression is read.
**
**  'IntrForEndBody' is an action to interpret a for-statement.  It is called
**  when the  reader encounters the  end of the  statement body.  <nr> is the
**  number of statements in the body.
**
**  'IntrForEnd' is an  action  to interpret a  for-statement.   It is called
**  when the  reader encounters the end of  the statement,  i.e., immediately
**  after 'IntrForEndBody'.
**
**  Since loops cannot be interpreted immediately,  the interpreter calls the
**  coder  to create a  procedure (with no arguments) and  calls that.
*/

void IntrForBegin(IntrState * intr, Obj stackNams)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    if (intr->coding == 0)
        StartFakeFuncExpr(intr, stackNams);

    intr->coding++;

    // code a for loop
    CodeForBegin(intr->cs);
}

void IntrForIn(IntrState * intr)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeForIn(intr->cs);
}

void IntrForBeginBody(IntrState * intr)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeForBeginBody(intr->cs);
}

void IntrForEndBody(IntrState * intr, UInt nr)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeForEndBody(intr->cs, nr);
}

void IntrForEnd(IntrState * intr, Obj stackNams)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);

    intr->coding--;
    CodeForEnd(intr->cs);

    if (intr->coding == 0)
        FinishAndCallFakeFuncExpr(intr, stackNams);
}


/****************************************************************************
**
*F  IntrWhileBegin()  . . . . . interpret while-statement, begin of statement
*F  IntrWhileBeginBody()  . . . . .  interpret while-statement, begin of body
*F  IntrWhileEndBody(<nr>)  . . . . .  interpret while-statement, end of body
*F  IntrWhileEnd()  . . . . . . . interpret while-statement, end of statement
**
**  'IntrWhileBegin' is   an action to  interpret   a while-statement.  It is
**  called when the    reader encounters the    'while', i.e., *before*   the
**  condition is read.
**
**  'IntrWhileBeginBody' is an action  to interpret a while-statement.  It is
**  called when the reader encounters  the  beginning of the statement  body,
**  i.e., *after* the condition is read.
**
**  'IntrWhileEndBody' is  an action to interpret   a while-statement.  It is
**  called when the reader encounters the end of the statement body.  <nr> is
**  the number of statements in the body.
**
**  'IntrWhileEnd' is an action to interpret a while-statement.  It is called
**  when  the reader encounters  the  end of  the  statement, i.e., immediate
**  after 'IntrWhileEndBody'.
**
**  Since loops cannot be interpreted immediately,  the interpreter calls the
**  coder  to create a  procedure (with no arguments) and  calls that.
*/

void IntrWhileBegin(IntrState * intr, Obj stackNams)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    if (intr->coding == 0)
        StartFakeFuncExpr(intr, stackNams);

    intr->coding++;

    // code a while loop
    CodeWhileBegin(intr->cs);
}

void IntrWhileBeginBody(IntrState * intr)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeWhileBeginBody(intr->cs);
}

void IntrWhileEndBody(IntrState * intr, UInt nr)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeWhileEndBody(intr->cs, nr);
}

void IntrWhileEnd(IntrState * intr, Obj stackNams)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);

    intr->coding--;
    CodeWhileEnd(intr->cs);

    if (intr->coding == 0)
        FinishAndCallFakeFuncExpr(intr, stackNams);
}


/****************************************************************************
**
*F  IntrQualifiedExprBegin(<qual>) .  interpret readonly/readwrite expr start
*F  IntrQualifiedExprEnd() . . . . . .  interpret readonly/readwrite expr end
**
**  These functions interpret the beginning and end of the readonly/readwrite
**  qualified expressions of an atomic statement.
*/

void IntrQualifiedExprBegin(IntrState * intr, UInt qual)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeQualifiedExprBegin(intr->cs, qual);
}

void IntrQualifiedExprEnd(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeQualifiedExprEnd(intr->cs);
}

/****************************************************************************
**
*F  IntrAtomicBegin() . . . .  interpret atomic-statement, begin of statement
*F  IntrAtomicBeginBody(<nrexprs>)  interpret atomic-statement, begin of body
*F  IntrAtomicEndBody(<nrstats>) . .  interpret atomic-statement, end of body
*F  IntrAtomicEnd() . . . . . .  interpret atomic-statement, end of statement
**
**  'IntrAtomicBegin' is an action to interpret an atomic-statement. It is
**  called when the reader encounters the 'atomic', i.e., *before* the
**  expressions to be locked are read.
**
**  'IntrAtomicBeginBody' is an action to interpret an atomic-statement. It
**  is called when the reader encounters the beginning of the statement body,
**  i.e., *after* the expressions to be locked are read. <nrexprs> is the
**  number of expressions to be locked
**
**  'IntrAtomicEndBody' is an action to interpret an atomic-statement. It is
**  called when the reader encounters the end of the statement body.
**  <nrstats> is the number of statements in the body.
**
**  'IntrAtomicEnd' is an action to interpret an atomic-statement. It is
**  called when the reader encounters the end of the statement, i.e.,
**  immediately after 'IntrAtomicEndBody'.
**
**  These functions only do something meaningful inside HPC-GAP; in plain
**  GAP, they are simply placeholders.
*/

void IntrAtomicBegin(IntrState * intr, Obj stackNams)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    if (intr->coding == 0)
        StartFakeFuncExpr(intr, stackNams);

    intr->coding++;

    CodeAtomicBegin(intr->cs);
}

void IntrAtomicBeginBody(IntrState * intr, UInt nrexprs)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeAtomicBeginBody(intr->cs, nrexprs);
}

void IntrAtomicEndBody(IntrState * intr, Int nrstats)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeAtomicEndBody(intr->cs, nrstats);
}

void IntrAtomicEnd(IntrState * intr, Obj stackNams)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);

    intr->coding--;
    CodeAtomicEnd(intr->cs);

    if (intr->coding == 0)
        FinishAndCallFakeFuncExpr(intr, stackNams);
}


/****************************************************************************
**
*F  IntrRepeatBegin() . . . .  interpret repeat-statement, begin of statement
*F  IntrRepeatBeginBody() . . . . . interpret repeat-statement, begin of body
*F  IntrRepeatEndBody(<nr>) . . . . . interpret repeat-statement, end of body
*F  IntrRepeatEnd() . . . . . .  interpret repeat-statement, end of statement
**
**  'IntrRepeatBegin"  is an action to interpret  a  repeat-statement.  It is
**  called when the read encounters the 'repeat'.
**
**  'IntrRepeatBeginBody' is an action  to interpret a  repeat-statement.  It
**  is called when the reader encounters the beginning of the statement body,
**  i.e., immediately after 'IntrRepeatBegin'.
**
**  'IntrRepeatEndBody' is an action  to interpret a repeat-statement.  It is
**  called when the reader  encounters the end of  the statement  body, i.e.,
**  *before* the condition is read.  <nr> is the  number of statements in the
**  body.
**
**  'IntrRepeatEnd' is  an  action to interpret  a repeat-statement.    It is
**  called when the reader encounters the end of the statement, i.e., *after*
**  the condition is read.
**
**  Since loops cannot be interpreted immediately,  the interpreter calls the
**  coder  to create a  procedure (with no arguments) and  calls that.
*/

void IntrRepeatBegin(IntrState * intr, Obj stackNams)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    if (intr->coding == 0)
        StartFakeFuncExpr(intr, stackNams);

    intr->coding++;

    // code a repeat loop
    CodeRepeatBegin(intr->cs);
}

void IntrRepeatBeginBody(IntrState * intr)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeRepeatBeginBody(intr->cs);
}

void IntrRepeatEndBody(IntrState * intr, UInt nr)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeRepeatEndBody(intr->cs, nr);
}

void IntrRepeatEnd(IntrState * intr, Obj stackNams)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);

    intr->coding--;
    CodeRepeatEnd(intr->cs);

    if (intr->coding == 0)
        FinishAndCallFakeFuncExpr(intr, stackNams);
}


/****************************************************************************
**
*F  IntrBreak() . . . . . . . . . . . . . . . . . . interpret break-statement
**
**  'IntrBreak'  is the action to interpret  a break-statement.  It is called
**  when the reader encounters a 'break;'.
**
**  Break-statements are  always coded (if  they are not ignored), since they
**  can only appear in loops.
*/

void IntrBreak(IntrState * intr)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeBreak(intr->cs);
}


/****************************************************************************
**
*F  IntrContinue() . . . . . . . . . . . . . . . interpret continue-statement
**
**  'IntrContinue' is the action to interpret a continue-statement. It is
**  called when the reader encounters a 'continue;'.
**
**  Continue-statements are always coded (if they are not ignored), since
**  they can only appear in loops.
*/

void IntrContinue(IntrState * intr)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    GAP_ASSERT(intr->coding > 0);
    CodeContinue(intr->cs);
}


/****************************************************************************
**
*F  IntrReturnObj() . . . . . . . . . . . .  interpret return-value-statement
**
**  'IntrReturnObj' is the action  to interpret a return-value-statement.  It
**  is  called when  the reader encounters  a  'return  <expr>;', but *after*
**  reading the expression <expr>.
*/

void IntrReturnObj(IntrState * intr)
{
    Obj                 val;            // return value

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeReturnObj(intr->cs);
        return;
    }


    // empty the values stack and push the return value
    val = PopObj(intr);
    SET_LEN_PLIST(intr->StackObj, 0);
    PushObj(intr, val);

    // indicate that a return-statement was interpreted
    intr->returning = STATUS_RETURN;
}


/****************************************************************************
**
*F  IntrReturnVoid()  . . . . . . . . . . . . interpret return-void-statement
**
**  'IntrReturnVoid' is the action to interpret  a return-void-statement.  It
**  is called when the reader encounters a 'return;'.
*/

void IntrReturnVoid(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeReturnVoid(intr->cs);
        return;
    }


    // empty the values stack and push the void value
    SET_LEN_PLIST(intr->StackObj, 0);
    PushVoidObj(intr);

    // indicate that a return-statement was interpreted
    intr->returning = STATUS_RETURN;
}


/****************************************************************************
**
*F  IntrQuit()  . . . . . . . . . . . . . . . . . .  interpret quit-statement
**
**  'IntrQuit' is the  action to interpret   a quit-statement.  It  is called
**  when the reader encounters a 'quit;'.
*/

void IntrQuit(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // 'quit' is not allowed in functions (by the reader)
    GAP_ASSERT(intr->coding == 0);

    // empty the values stack and push the void value
    SET_LEN_PLIST(intr->StackObj, 0);
    PushVoidObj(intr);

    // indicate that a quit-statement was interpreted
    intr->returning = STATUS_QUIT;
}

/****************************************************************************
**
*F  IntrQUIT()  . . . . . . . . . . . . . . . . . .  interpret quit-statement
**
**  'IntrQUIT' is the  action to interpret   a quit-statement.  It  is called
**  when the reader encounters a 'QUIT;'.
*/

void IntrQUIT(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // 'QUIT' is not allowed in functions (by the reader)
    GAP_ASSERT(intr->coding == 0);

    // empty the values stack and push the void value
    SET_LEN_PLIST(intr->StackObj, 0);
    PushVoidObj(intr);

    // indicate that a QUIT-statement was interpreted
    intr->returning = STATUS_QQUIT;
}

/****************************************************************************
**
*F  IntrHelp()
**
**  'IntrHelp' is the action to interpret a help statement.
**
*/

void IntrHelp(IntrState * intr, Obj topic)
{
    UInt hgvar;
    Obj  help;
    Obj  res;

    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // '?' is not allowed in functions (by the reader)
    GAP_ASSERT(intr->coding == 0);

    // FIXME: Hard coded function name
    hgvar = GVarName("HELP");
    help = ValGVar(hgvar);
    if (!help) {
        ErrorQuit(
            "Global variable \"HELP\" is not defined. Cannot access help", 0,
            0);
    }
    if (!IS_FUNC(help)) {
        ErrorQuit(
            "Global variable \"HELP\" is not a function. Cannot access help",
            0, 0);
    }

    res = CALL_1ARGS(help, topic);
    if (res)
        PushObj(intr, res);
    else
        PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrOrL() . . . . . . . . . .  interpret or-expression, left operand read
*F  IntrOr()  . . . . . . . . . . interpret or-expression, right operand read
**
**  'IntrOrL' is an action to interpret an or-expression.   It is called when
**  the reader encounters the 'or' keyword, i.e., *after* the left operand is
**  read but *before* the right operand is read.
**
**  'IntrOr' is an action to  interpret an or-expression.   It is called when
**  the reader encountered  the  end of  the  expression, i.e., *after*  both
**  operands are read.
*/

void IntrOrL(IntrState * intr)
{
    Obj                 opL;            // value of left operand

    // ignore or code
    SKIP_IF_RETURNING();
    if (intr->ignoring > 0) {
        intr->ignoring++;
        return;
    }
    if (intr->coding > 0) {
        CodeOrL(intr->cs);
        return;
    }


    // if the left operand is 'true', ignore the right operand
    opL = PopObj(intr);
    PushObj(intr, opL);
    if ( opL == True ) {
        PushObj(intr, opL);
        intr->ignoring = 1;
    }
}

void IntrOr(IntrState * intr)
{
    Obj                 opL;            // value of left  operand
    Obj                 opR;            // value of right operand

    // ignore or code
    SKIP_IF_RETURNING();
    if (intr->ignoring > 1) {
        intr->ignoring--;
        return;
    }
    if (intr->coding > 0) {
        CodeOr(intr->cs);
        return;
    }


    // stop ignoring things now
    intr->ignoring = 0;

    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // if the left operand is 'true', this is the result
    if      ( opL == True ) {
        PushObj(intr, opL);
    }

    // if the left operand is 'false', the result is the right operand
    else if ( opL == False  ) {
        if ( opR == True || opR == False  ) {
            PushObj(intr, opR);
        }
        else {
            RequireArgumentEx(0, opR, "<expr>""must be 'true' or 'false'");
        }
    }

    // signal an error
    else {
        RequireArgumentEx(0, opL, "<expr>""must be 'true' or 'false'");
    }
}


/****************************************************************************
**
*F  IntrAndL()  . . . . . . . . . interpret and-expression, left operand read
*F  IntrAnd() . . . . . . . . .  interpret and-expression, right operand read
**
**  'IntrAndL' is  an action  to interpret an   and-expression.  It is called
**  when the reader  encounters the  'and'  keyword, i.e., *after*  the  left
**  operand is read but *before* the right operand is read.
**
**  'IntrAnd' is an action to interpret an and-expression.  It is called when
**  the reader encountered   the end of   the expression, i.e., *after*  both
**  operands are read.
*/

void IntrAndL(IntrState * intr)
{
    Obj                 opL;            // value of left operand

    // ignore or code
    SKIP_IF_RETURNING();
    if (intr->ignoring > 0) {
        intr->ignoring++;
        return;
    }
    if (intr->coding > 0) {
        CodeAndL(intr->cs);
        return;
    }


    // if the left operand is 'false', ignore the right operand
    opL = PopObj(intr);
    PushObj(intr, opL);
    if ( opL == False ) {
        PushObj(intr, opL);
        intr->ignoring = 1;
    }
}

void IntrAnd(IntrState * intr)
{
    Obj                 opL;            // value of left  operand
    Obj                 opR;            // value of right operand

    // ignore or code
    SKIP_IF_RETURNING();
    if (intr->ignoring > 1) {
        intr->ignoring--;
        return;
    }
    if (intr->coding > 0) {
        CodeAnd(intr->cs);
        return;
    }


    // stop ignoring things now
    intr->ignoring = 0;

    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // if the left operand is 'false', this is the result
    if      ( opL == False ) {
        PushObj(intr, opL);
    }

    // if the left operand is 'true', the result is the right operand
    else if ( opL == True  ) {
        if ( opR == False || opR == True  ) {
            PushObj(intr, opR);
        }
        else {
            RequireArgumentEx(0, opR, "<expr>""must be 'true' or 'false'");
        }
    }

    // handle the 'and' of two filters
    else if (IS_FILTER(opL)) {
        PushObj(intr, NewAndFilter(opL, opR));
    }

    // signal an error
    else {
        RequireArgumentEx(0, opL, "<expr>",
                          "must be 'true' or 'false' or a filter");
    }
}


/****************************************************************************
**
*F  IntrNot() . . . . . . . . . . . . . . . . . . .  interpret not-expression
**
**  'IntrNot' is the action to interpret a not-expression.  It is called when
**  the reader encounters a not-expression, *after* the operand is read.
*/

void IntrNot(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 op;             // operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeNot(intr->cs);
        return;
    }


    // get and check the operand
    op = PopObj(intr);
    if ( op != True && op != False ) {
        RequireArgumentEx(0, op, "<expr>""must be 'true' or 'false'");
    }

    // negate the operand
    val = (op == False ? True : False);

    // push the result
    PushObj(intr, val);
}


/****************************************************************************
**
*F  IntrEq()  . . . . . . . . . . . . . . . . . . . .  interpret =-expression
*F  IntrNe()  . . . . . . . . . . . . . . . . . . . . interpret <>-expression
*F  IntrLt()  . . . . . . . . . . . . . . . . . . . . interpret  <-expression
*F  IntrGe()  . . . . . . . . . . . . . . . . . . . . interpret >=-expression
*F  IntrGt()  . . . . . . . . . . . . . . . . . . . .  interpret >-expression
*F  IntrLe()  . . . . . . . . . . . . . . . . . . . . interpret <=-expression
**
**  'IntrEq', 'IntrNe', 'IntrLt', 'IntrGe', 'IntrGt',   and 'IntrLe' are  the
**  actions to interpret the respective operator expression.  They are called
**  by the reader *after* *both* operands are read.
*/

static void StackSwap(IntrState * intr)
{
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // push the operands in reverse order
    PushObj(intr, opR);
    PushObj(intr, opL);
}

void IntrEq(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeEq(intr->cs);
        return;
    }


    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // compare them
    val = (EQ( opL, opR ) ? True : False);

    // push the result
    PushObj(intr, val);
}

void IntrNe(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeNe(intr->cs);
        return;
    }


    // '<left> <> <right>' is 'not <left> = <right>'
    IntrEq(intr);
    IntrNot(intr);
}

void IntrLt(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeLt(intr->cs);
        return;
    }


    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // compare them
    val = (LT( opL, opR ) ? True : False);

    // push the result
    PushObj(intr, val);
}

void IntrGe(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeGe(intr->cs);
        return;
    }


    // '<left> >= <right>' is 'not <left> < <right>'
    IntrLt(intr);
    IntrNot(intr);
}

void IntrGt(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeGt(intr->cs);
        return;
    }


    // '<left> > <right>' is '<right> < <left>'
    StackSwap(intr);
    IntrLt(intr);
}

void IntrLe(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeLe(intr->cs);
        return;
    }


    // '<left> <= <right>' is 'not <right> < <left>'
    StackSwap(intr);
    IntrLt(intr);
    IntrNot(intr);
}


/****************************************************************************
**
*F  IntrIn()  . . . . . . . . . . . . . . . . . . . . interpret in-expression
**
**  'IntrIn'  is the action  to interpret an  in-expression.  It is called by
**  the reader *after* *both* operands are read.
*/

void IntrIn(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIn(intr->cs);
        return;
    }


    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // perform the test
    val = (IN( opL, opR ) ? True : False);

    // push the result
    PushObj(intr, val);
}


/****************************************************************************
**
*F  IntrSum() . . . . . . . . . . . . . . . . . . . .  interpret +-expression
*F  IntrAInv()  . . . . . . . . . . . . . . . .  interpret unary --expression
*F  IntrDiff()  . . . . . . . . . . . . . . . . . . .  interpret --expression
*F  IntrProd()  . . . . . . . . . . . . . . . . . . .  interpret *-expression
*F  IntrQuo() . . . . . . . . . . . . . . . . . . . .  interpret /-expression
*F  IntrMod()   . . . . . . . . . . . . . . . . . .  interpret mod-expression
*F  IntrPow() . . . . . . . . . . . . . . . . . . . .  interpret ^-expression
**
**  'IntrSum', 'IntrDiff',  'IntrProd',  'IntrQuo',  'IntrMod', and 'IntrPow'
**  are  the actions to interpret  the  respective operator expression.  They
**  are called by the reader *after* *both* operands are read.
*/

void IntrSum(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeSum(intr->cs);
        return;
    }


    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // compute the sum
    val = SUM( opL, opR );

    // push the result
    PushObj(intr, val);
}

void IntrAInv(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAInv(intr->cs);
        return;
    }


    // get the operand
    opL = PopObj(intr);

    // compute the additive inverse
    val = AINV_SAMEMUT(opL);

    // push the result
    PushObj(intr, val);
}

void IntrDiff(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeDiff(intr->cs);
        return;
    }


    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // compute the difference
    val = DIFF( opL, opR );

    // push the result
    PushObj(intr, val);
}

void IntrProd(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeProd(intr->cs);
        return;
    }


    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // compute the product
    val = PROD( opL, opR );

    // push the result
    PushObj(intr, val);
}

void IntrQuo(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeQuo(intr->cs);
        return;
    }


    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // compute the quotient
    val = QUO( opL, opR );

    // push the result
    PushObj(intr, val);
}

void IntrMod(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeMod(intr->cs);
        return;
    }


    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // compute the remainder
    val = MOD( opL, opR );

    // push the result
    PushObj(intr, val);
}

void IntrPow(IntrState * intr)
{
    Obj                 val;            // value, result
    Obj                 opL;            // left operand
    Obj                 opR;            // right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodePow(intr->cs);
        return;
    }


    // get the operands
    opR = PopObj(intr);
    opL = PopObj(intr);

    // compute the power
    val = POW( opL, opR );

    // push the result
    PushObj(intr, val);
}


/****************************************************************************
**
*F  IntrIntExpr(<str>)  . . . . . . . .  interpret literal integer expression
**
**  'IntrIntExpr' is the action  to  interpret a literal  integer expression.
**  <str> is the integer as a (null terminated) C character string.
*/

void IntrIntExpr(IntrState * intr, Obj string, Char * str)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    Obj val = IntStringInternal(string, str);
    GAP_ASSERT(val != Fail);

    if (intr->coding > 0) {
        CodeIntExpr(intr->cs, val);
    }
    else {
        // push the integer value
        PushObj(intr, val);
    }
}


/****************************************************************************
**
*F  IntrFloatExpr(<str>)  . . . . . . . .  interpret literal float expression
**
**  'IntrFloatExpr' is the action  to  interpret a literal  float expression.
**  <str> is the float as a (null terminated) C character string.
*/


static Obj CONVERT_FLOAT_LITERAL_EAGER;

static Obj ConvertFloatLiteralEager(Obj str)
{
    Char * chars = (Char *)CHARS_STRING(str);
    UInt   len = GET_LEN_STRING(str);
    Char   mark = '\0';
    if (chars[len - 1] == '_') {
        SET_LEN_STRING(str, len - 1);
        chars[len - 1] = '\0';
    }
    else if (chars[len - 2] == '_') {
        mark = chars[len - 1];
        SET_LEN_STRING(str, len - 2);
        chars[len - 2] = '\0';
    }
    Obj res = CALL_2ARGS(CONVERT_FLOAT_LITERAL_EAGER, str, ObjsChar[(UInt)mark]);
    if (res == Fail)
        ErrorQuit("failed to convert float literal", 0, 0);
    return res;
}

void IntrFloatExpr(IntrState * intr, Obj string, Char * str)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (string == 0)
        string = MakeString(str);
    if (intr->coding > 0) {
        CodeFloatExpr(intr->cs, string);
        return;
    }

    PushObj(intr, ConvertFloatLiteralEager(string));
}


/****************************************************************************
**
*F   IntrIntObjExpr()  . . . . . . .  'interpret' a GAP small integer
**
**  'IntrIntObjExpr' is the action to 'interpret' a existing GAP small
**  integer. This is used for implementing constants.
*/

void IntrIntObjExpr(IntrState * intr, Obj val)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIntExpr(intr->cs, val);
        return;
    }


    // push the value
    PushObj(intr, val);
}

/****************************************************************************
**
*F  IntrTrueExpr()  . . . . . . . . . . . . interpret literal true expression
**
**  'IntrTrueExpr' is the action to interpret a literal true expression.
*/

void IntrTrueExpr(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeTrueExpr(intr->cs);
        return;
    }


    // push the value
    PushObj(intr, True);
}


/****************************************************************************
**
*F  IntrFalseExpr() . . . . . . . . . . .  interpret literal false expression
**
**  'IntrFalseExpr' is the action to interpret a literal false expression.
*/

void IntrFalseExpr(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeFalseExpr(intr->cs);
        return;
    }


    // push the value
    PushObj(intr, False);
}


/****************************************************************************
**
*F  IntrTildeExpr() . . . . . . . . . . . . . . .  interpret tilde expression
**
**  'IntrTildeExpr' is the action to interpret a tilde expression.
**
*/

void IntrTildeExpr(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeTildeExpr(intr->cs);
        return;
    }

    if (!STATE(Tilde)) {
        // this code should be impossible to reach, the parser won't allow us
        // to get here; but we leave it here out of paranoia
        ErrorQuit("'~' does not have a value here", 0, 0);
    }

    // push the value
    PushObj(intr, STATE(Tilde));
}


/****************************************************************************
**
*F  IntrCharExpr(<chr>) . . . . . . .  interpret literal character expression
**
**  'IntrCharExpr' is the action to interpret a literal character expression.
**  <chr> is the C character.
*/

void IntrCharExpr(IntrState * intr, Char chr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeCharExpr(intr->cs, chr);
        return;
    }


    // push the value
    PushObj(intr, ObjsChar[(UChar)chr]);
}


/****************************************************************************
**
*F  IntrPermCycle(<nr>) . . . . . .  interpret literal permutation expression
*F  IntrPerm(<nr>)  . . . . . . . .  interpret literal permutation expression
*/

static Obj GetFromStack(Obj cycle, Int j)
{
    IntrState * intr = (IntrState *)cycle;
    return PopObj(intr);
}

void IntrPermCycle(IntrState * intr, UInt nrx, UInt nrc)
{
    Obj                 perm;           // permutation
    UInt                m;              // maximal entry in permutation

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodePermCycle(intr->cs, nrx, nrc);
        return;
    }


    // get the permutation (allocate for the first cycle)
    if ( nrc == 1 ) {
        m = 0;
        perm = NEW_PERM4( 0 );
    }
    else {
        const UInt countObj = LEN_PLIST(intr->StackObj);
        m = INT_INTOBJ(ELM_LIST(intr->StackObj, countObj - nrx));
        perm = ELM_LIST(intr->StackObj, countObj - nrx - 1);
    }

    m = ScanPermCycle(perm, m, (Obj)intr, nrx, GetFromStack);

    // push the permutation (if necessary, drop permutation first)
    if (nrc != 1) {
        PopObj(intr);
        PopObj(intr);
    }
    PushObj(intr, perm);
    PushObj(intr, INTOBJ_INT(m));
}

void IntrPerm(IntrState * intr, UInt nrc)
{
    Obj                 perm;           // permutation, result
    UInt                m;              // maximal entry in permutation

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodePerm(intr->cs, nrc);
        return;
    }


    // special case for identity permutation
    if ( nrc == 0 ) {
        perm = NEW_PERM2( 0 );
    }

    // otherwise
    else {

        // get the permutation and its maximal entry
        m = INT_INTOBJ(PopObj(intr));
        perm = PopObj(intr);

        // if possible represent the permutation with short entries
        TrimPerm(perm, m);
    }

    // push the result
    PushObj(intr, perm);
}


/****************************************************************************
**
*F  IntrListExprBegin(<top>)  . . . . . . . . . .  interpret list expr, begin
*F  IntrListExprBeginElm(<pos>) . . . . .  interpret list expr, begin element
*F  IntrListExprEndElm()  . . . . . . . . .  interpret list expr, end element
*F  IntrListExprEnd(<nr>,<range>,<top>,<tilde>) . .  interpret list expr, end
*/

void IntrListExprBegin(IntrState * intr, UInt top)
{
    Obj                 list;           // new list
    Obj                 old;            // old value of '~'

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeListExprBegin(intr->cs, top);
        return;
    }


    // allocate the new list
    list = NewEmptyPlist();

    // if this is an outmost list, save it for reference in '~'
    // (and save the old value of '~' on the values stack)
    if ( top ) {
        old = STATE(Tilde);
        if (old != 0) {
            PushObj(intr, old);
        }
        else {
            PushVoidObj(intr);
        }
        STATE(Tilde) = list;
    }

    // push the list
    PushObj(intr, list);
}

void IntrListExprBeginElm(IntrState * intr, UInt pos)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeListExprBeginElm(intr->cs, pos);
        return;
    }


    // remember this position on the values stack
    PushObj(intr, INTOBJ_INT(pos));
}

void IntrListExprEndElm(IntrState * intr)
{
    Obj                 list;           // list that is currently made
    Obj                 pos;            // position
    UInt                p;              // position, as a C integer
    Obj                 val;            // value to assign into list

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeListExprEndElm(intr->cs);
        return;
    }


    // get the value
    val = PopObj(intr);

    // get the position
    pos = PopObj(intr);
    p = INT_INTOBJ( pos );

    // get the list
    list = PopObj(intr);

    // assign the element into the list
    ASS_LIST( list, p, val );

    // push the list again
    PushObj(intr, list);
}

void IntrListExprEnd(
    IntrState * intr, UInt nr, UInt range, UInt top, UInt tilde)
{
    Obj                 list;           // the list, result
    Obj                 old;            // old value of '~'
    Int                 low;            // low value of range
    Int                 inc;            // increment of range
    Int                 high;           // high value of range
    Obj                 val;            // temporary value

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeListExprEnd(intr->cs, nr, range, top, tilde);
        return;
    }


    // if this was a top level expression, restore the value of '~'
    if ( top ) {
        list = PopObj(intr);
        old = PopVoidObj(intr);
        STATE(Tilde) = old;
        PushObj(intr, list);
    }

    // if this was a range, convert the list to a range
    if ( range ) {
        // get the list
        list = PopObj(intr);

        // get the low value
        val = ELM_LIST( list, 1 );
        low = GetSmallIntEx("Range", val, "<first>");

        // get the increment
        if ( nr == 3 ) {
            val = ELM_LIST( list, 2 );
            Int v = GetSmallIntEx("Range", val, "<second>");
            if ( v == low ) {
                ErrorQuit("Range: <second> must not be equal to <first> (%d)",
                          (Int)low, 0);
            }
            inc = v - low;
        }
        else {
            inc = 1;
        }

        // get and check the high value
        val = ELM_LIST( list, LEN_LIST(list) );
        Int v = GetSmallIntEx("Range", val, "<last>");
        if ( (v - low) % inc != 0 ) {
            ErrorQuit(
                "Range: <last>-<first> (%d) must be divisible by <inc> (%d)",
                (Int)(v-low), (Int)inc );
        }
        high = v;

        // if <low> is larger than <high> the range is empty
        if ( (0 < inc && high < low) || (inc < 0 && low < high) ) {
            list = NewEmptyPlist();
        }

        // if <low> is equal to <high> the range is a singleton list
        else if ( low == high ) {
            list = NEW_PLIST( T_PLIST_CYC_SSORT, 1 );
            SET_LEN_PLIST( list, 1 );
            SET_ELM_PLIST( list, 1, INTOBJ_INT(low) );
        }

        // else make the range
        else {
            // length must be a small integer as well
            if ((high-low) / inc >= INT_INTOBJ_MAX) {
                ErrorQuit("Range: the length of a range must be a small integer",
                           0, 0);
            }

            list = NEW_RANGE((high - low) / inc + 1, low, inc);
        }

        // push the list again
        PushObj(intr, list);
    }
    else {
        // give back unneeded memory
        list = PopObj(intr);
        // Might have transformed into another type of list
        if (IS_PLIST(list)) {
            SHRINK_PLIST(list, LEN_PLIST(list));
        }
        PushObj(intr, list);
    }
}


/****************************************************************************
**
*F  IntrStringExpr(<str>) . . . . . . . . interpret literal string expression
*/

void IntrStringExpr(IntrState * intr, Obj string)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeStringExpr(intr->cs, string);
        return;
    }


    // push the string, already newly created
    PushObj(intr, string);
}

void IntrPragma(IntrState * intr, Obj pragma)
{
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodePragma(intr->cs, pragma);
    }
    else {
        // Push a void when interpreting
        PushVoidObj(intr);
    }
}

/****************************************************************************
**
*F  IntrRecExprBegin(<top>) . . . . . . . . . .  interpret record expr, begin
*F  IntrRecExprBeginElmName(<rnam>) . .  interpret record expr, begin element
*F  IntrRecExprBeginElmExpr() . . . . .  interpret record expr, begin element
*F  IntrRecExprEndElmExpr() . . . . . . .  interpret record expr, end element
*F  IntrRecExprEnd(<nr>,<top>,<tilde>)  . . . . .  interpret record expr, end
*/

void IntrRecExprBegin(IntrState * intr, UInt top)
{
    Obj                 record;         // new record
    Obj                 old;            // old value of '~'

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeRecExprBegin(intr->cs, top);
        return;
    }


    // allocate the new record
    record = NEW_PREC( 0 );

    // if this is an outmost record, save it for reference in '~'
    // (and save the old value of '~' on the values stack)
    if ( top ) {
        old = STATE(Tilde);
        if (old != 0) {
            PushObj(intr, old);
        }
        else {
            PushVoidObj(intr);
        }
        STATE(Tilde) = record;
    }

    // push the record
    PushObj(intr, record);
}

void IntrRecExprBeginElmName(IntrState * intr, UInt rnam)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeRecExprBeginElmName(intr->cs, rnam);
        return;
    }


    // remember the name on the values stack
    PushObj(intr, (Obj)rnam);
}

void IntrRecExprBeginElmExpr(IntrState * intr)
{
    UInt                rnam;           // record name

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeRecExprBeginElmExpr(intr->cs);
        return;
    }


    // convert the expression to a record name
    rnam = RNamObj(PopObj(intr));

    // remember the name on the values stack
    PushObj(intr, (Obj)rnam);
}

void IntrRecExprEndElm(IntrState * intr)
{
    Obj                 record;         // record that is currently made
    UInt                rnam;           // name of record element
    Obj                 val;            // value of record element

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeRecExprEndElm(intr->cs);
        return;
    }


    // get the value
    val = PopObj(intr);

    // get the record name
    rnam = (UInt)PopObj(intr);

    // get the record
    record = PopObj(intr);

    // assign the value into the record
    ASS_REC( record, rnam, val );

    // push the record again
    PushObj(intr, record);
}

void IntrRecExprEnd(IntrState * intr, UInt nr, UInt top, UInt tilde)
{
    Obj                 record;         // record that is currently made
    Obj                 old;            // old value of '~'

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeRecExprEnd(intr->cs, nr, top, tilde);
        return;
    }


    // if this was a top level expression, restore the value of '~'
    if ( top ) {
        record = PopObj(intr);
        old = PopVoidObj(intr);
        STATE(Tilde) = old;
        PushObj(intr, record);
    }
}

/****************************************************************************
**
*F  IntrFuncCallOptionsBegin() . . . .. . . . . .  interpret options, begin
*F  IntrFuncCallOptionsBeginElmName(<rnam>).  interpret options, begin element
*F  IntrFuncCallOptionsBeginElmExpr() . .. .  interpret options, begin element
*F  IntrFuncCallOptionsEndElm() . . .. .  . .  interpret options, end element
*F  IntrFuncCallOptionsEndElmEmpty() .. .  . .  interpret options, end element
*F  IntrFuncCallOptionsEnd(<nr>)  . . . . . . . .  interpret options, end
**
**  The net effect of all of these is to leave a record object on the stack
**  where IntrFuncCallEnd can use it
*/

void IntrFuncCallOptionsBegin(IntrState * intr)
{
    Obj                 record;         // new record

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeFuncCallOptionsBegin(intr->cs);
        return;
    }


    // allocate the new record
    record = NEW_PREC( 0 );
    // push the record
    PushObj(intr, record);
}

void IntrFuncCallOptionsBeginElmName(IntrState * intr, UInt rnam)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeFuncCallOptionsBeginElmName(intr->cs, rnam);
        return;
    }


    // remember the name on the values stack
    PushObj(intr, (Obj)rnam);
}

void IntrFuncCallOptionsBeginElmExpr(IntrState * intr)
{
    UInt                rnam;           // record name

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeFuncCallOptionsBeginElmExpr(intr->cs);
        return;
    }


    // convert the expression to a record name
    rnam = RNamObj(PopObj(intr));

    // remember the name on the values stack
    PushObj(intr, (Obj)rnam);
}

void IntrFuncCallOptionsEndElm(IntrState * intr)
{
    Obj                 record;         // record that is currently made
    UInt                rnam;           // name of record element
    Obj                 val;            // value of record element

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeFuncCallOptionsEndElm(intr->cs);
        return;
    }


    // get the value
    val = PopObj(intr);

    // get the record name
    rnam = (UInt)PopObj(intr);

    // get the record
    record = PopObj(intr);

    // assign the value into the record
    ASS_REC( record, rnam, val );

    // push the record again
    PushObj(intr, record);
}

void IntrFuncCallOptionsEndElmEmpty(IntrState * intr)
{
    Obj                 record;         // record that is currently made
    UInt                rnam;           // name of record element
    Obj                 val;            // value of record element

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeFuncCallOptionsEndElmEmpty(intr->cs);
        return;
    }


    // get the value
    val = True;

    // get the record name
    rnam = (UInt)PopObj(intr);

    // get the record
    record = PopObj(intr);

    // assign the value into the record
    ASS_REC( record, rnam, val );

    // push the record again
    PushObj(intr, record);
}

void IntrFuncCallOptionsEnd(IntrState * intr, UInt nr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeFuncCallOptionsEnd(intr->cs, nr);
        return;
    }
}


/****************************************************************************
**
*F  IntrAssLVar(<lvar>) . . . . . . . . . . . . interpret assignment to local
*/

void IntrAssLVar(IntrState * intr, UInt lvar)
{
  Obj val;
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    if (intr->coding > 0)
        CodeAssLVar(intr->cs, lvar);

    // Or in the break loop
    else {
        val = PopObj(intr);
        ASS_LVAR(lvar, val);
        PushObj(intr, val);
    }
}

void IntrUnbLVar(IntrState * intr, UInt lvar)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    if (intr->coding > 0)
        CodeUnbLVar(intr->cs, lvar);

    // or in the break loop
    else {
        ASS_LVAR(lvar,0);
        PushVoidObj(intr);
    }
}


/****************************************************************************
**
*F  IntrRefLVar(<lvar>) . . . . . . . . . . . .  interpret reference to local
*/

void IntrRefLVar(IntrState * intr, UInt lvar)
{
  Obj val;
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    if (intr->coding > 0)
        CodeRefLVar(intr->cs, lvar);

    // or in the break loop

    else {
        val = OBJ_LVAR(lvar);
        if (val == 0) {
            ErrorMayQuit("Variable: '%g' must have an assigned value",
                         (Int)NAME_LVAR(lvar), 0);
        }
        PushObj(intr, val);
    }
}

void IntrIsbLVar(IntrState * intr, UInt lvar)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    if (intr->coding > 0)
        CodeIsbLVar(intr->cs, lvar);

    // or debugging
    else {
        PushObj(intr, OBJ_LVAR(lvar) != (Obj)0 ? True : False);
    }
}


/****************************************************************************
**
*F  IntrAssHVar(<hvar>) . . . . . . . . . . .  interpret assignment to higher
*/

void IntrAssHVar(IntrState * intr, UInt hvar)
{
  Obj val;
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    if (intr->coding > 0)
        CodeAssHVar(intr->cs, hvar);
    // Or in the break loop
    else {
        val = PopObj(intr);
        ASS_HVAR(hvar, val);
        PushObj(intr, val);
    }
}

void IntrUnbHVar(IntrState * intr, UInt hvar)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    if (intr->coding > 0)
        CodeUnbHVar(intr->cs, hvar);
    // or debugging
    else {
        ASS_HVAR(hvar, 0);
        PushVoidObj(intr);
    }
}


/****************************************************************************
**
*F  IntrRefHVar(<hvar>) . . . . . . . . . . . . interpret reference to higher
*/

void IntrRefHVar(IntrState * intr, UInt hvar)
{
  Obj val;
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    if (intr->coding > 0)
        CodeRefHVar(intr->cs, hvar);
    // or debugging
    else {
        val = OBJ_HVAR(hvar);
        while (val == 0) {
            ErrorMayQuit("Variable: '%g' must have an assigned value",
                         (Int)NAME_HVAR((UInt)(hvar)), 0);
        }
        PushObj(intr, val);
    }
}

void IntrIsbHVar(IntrState * intr, UInt hvar)
{
    // ignore
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    // otherwise must be coding
    if (intr->coding > 0)
        CodeIsbHVar(intr->cs, hvar);
    // or debugging
    else
        PushObj(intr, (OBJ_HVAR(hvar) != (Obj)0) ? True : False);
}


/****************************************************************************
**
*F  IntrAssDVar(<dvar>) . . . . . . . . . . . . interpret assignment to debug
*/


void IntrAssDVar(IntrState * intr, UInt dvar, UInt depth)
{
    Obj                 rhs;            // right hand side
    Obj                 context;

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    if (intr->coding > 0) {
        ErrorQuit( "Variable: <debug-variable-%d-%d> cannot be used here",
                   dvar >> MAX_FUNC_LVARS_BITS, dvar & MAX_FUNC_LVARS_MASK );
    }


    // get the right hand side
    rhs = PopObj(intr);

    // assign the right hand side
    context = STATE(ErrorLVars);
    while (depth--)
      context = PARENT_LVARS(context);
    ASS_HVAR_WITH_CONTEXT(context, dvar, rhs);

    // push the right hand side again
    PushObj(intr, rhs);
}

void IntrUnbDVar(IntrState * intr, UInt dvar, UInt depth)
{
    Obj                 context;

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    if (intr->coding > 0) {
        ErrorQuit( "Variable: <debug-variable-%d-%d> cannot be used here",
                   dvar >> MAX_FUNC_LVARS_BITS, dvar & MAX_FUNC_LVARS_MASK );
    }

    // assign the right hand side
    context = STATE(ErrorLVars);
    while (depth--)
      context = PARENT_LVARS(context);
    ASS_HVAR_WITH_CONTEXT(context, dvar, (Obj)0);

    // push void
    PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrRefDVar(<dvar>) . . . . . . . . . . . .  interpret reference to debug
*/

void IntrRefDVar(IntrState * intr, UInt dvar, UInt depth)
{
    Obj                 val;            // value, result
    Obj                 context;

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    if (intr->coding > 0) {
        ErrorQuit( "Variable: <debug-variable-%d-%d> cannot be used here",
                   dvar >> MAX_FUNC_LVARS_BITS, dvar & MAX_FUNC_LVARS_MASK );
    }

    // get and check the value
    context = STATE(ErrorLVars);
    while (depth--)
      context = PARENT_LVARS(context);
    val = OBJ_HVAR_WITH_CONTEXT(context, dvar);
    if ( val == 0 ) {
        ErrorQuit( "Variable: <debug-variable-%d-%d> must have a value",
                   dvar >> MAX_FUNC_LVARS_BITS, dvar & MAX_FUNC_LVARS_MASK );
    }

    // push the value
    PushObj(intr, val);
}

void IntrIsbDVar(IntrState * intr, UInt dvar, UInt depth)
{
    Obj                 val;            // value, result
    Obj                 context;

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();

    if (intr->coding > 0) {
        ErrorQuit( "Variable: <debug-variable-%d-%d> cannot be used here",
                   dvar >> MAX_FUNC_LVARS_BITS, dvar & MAX_FUNC_LVARS_MASK );
    }

    // get the value
    context = STATE(ErrorLVars);
    while (depth--)
      context = PARENT_LVARS(context);
    val = OBJ_HVAR_WITH_CONTEXT(context, dvar);

    // push the value
    PushObj(intr, val != 0 ? True : False);
}


/****************************************************************************
**
*F  IntrAssGVar(<gvar>) . . . . . . . . . . .  interpret assignment to global
*/

void IntrAssGVar(IntrState * intr, UInt gvar)
{
    Obj                 rhs;            // right hand side

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
      CodeAssGVar(intr->cs, gvar);
      return;
    }


    // get the right hand side
    rhs = PopObj(intr);

    // assign the right hand side
    AssGVar( gvar, rhs );

    // push the right hand side again
    PushObj(intr, rhs);
}

void IntrUnbGVar(IntrState * intr, UInt gvar)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
      CodeUnbGVar(intr->cs, gvar);
      return;
    }


    // assign the right hand side
    AssGVar( gvar, (Obj)0 );

    // push void
    PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrRefGVar(<gvar>) . . . . . . . . . . . . interpret reference to global
*/

void IntrRefGVar(IntrState * intr, UInt gvar)
{
    Obj                 val;            // value, result

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
      CodeRefGVar(intr->cs, gvar);
      return;
    }


    // get and check the value
    if ( (val = ValAutoGVar( gvar )) == 0 ) {
        ErrorQuit("Variable: '%g' must have a value", (Int)NameGVar(gvar), 0);
    }

    // push the value
    PushObj(intr, val);
}

void IntrIsbGVar(IntrState * intr, UInt gvar)
{
    Obj                 val;            // value, result

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIsbGVar(intr->cs, gvar);
        return;
    }


    // get the value
    val = ValAutoGVar( gvar );

    // push the value
    PushObj(intr, val != 0 ? True : False);
}


/****************************************************************************
**
*F  IntrAssList() . . . . . . . . . . . . . .  interpret assignment to a list
*F  IntrAsssList()  . . . . . . . . . interpret multiple assignment to a list
*F  IntrAssListLevel(<level>) . . . . . interpret assignment to several lists
*F  IntrAsssListLevel(<level>)  . . intr multiple assignment to several lists
*/

void IntrAssList(IntrState * intr, Int narg)
{
    Obj                 list;           // list
    Obj                 pos;            // position
    Obj                 rhs;            // right hand side

    GAP_ASSERT(narg == 1 || narg == 2);

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAssList(intr->cs, narg);
        return;
    }

    // get the right hand side
    rhs = PopObj(intr);

    if (narg == 1) {
      // get the position
      pos = PopObj(intr);

      // get the list (checking is done by 'ASS_LIST' or 'ASSB_LIST')
      list = PopObj(intr);

      // assign to the element of the list
      if (IS_POS_INTOBJ(pos)) {
        ASS_LIST( list, INT_INTOBJ(pos), rhs );
      }
      else {
        ASSB_LIST(list, pos, rhs);
      }
    }
    else if (narg == 2) {
        Obj col = PopObj(intr);
        Obj row = PopObj(intr);
        list = PopObj(intr);

        ASS_MAT(list, row, col, rhs);
    }

    // push the right hand side again
    PushObj(intr, rhs);
}


void IntrAsssList(IntrState * intr)
{
    Obj                 list;           // list
    Obj                 poss;           // positions
    Obj                 rhss;           // right hand sides

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAsssList(intr->cs);
        return;
    }


    // get the right hand sides
    rhss = PopObj(intr);
    RequireDenseList("List Assignments", rhss);

    // get and check the positions
    poss = PopObj(intr);
    CheckIsPossList("List Assignments", poss);
    RequireSameLength("List Assignments", rhss, poss);

    // get the list (checking is done by 'ASSS_LIST')
    list = PopObj(intr);

    // assign to several elements of the list
    ASSS_LIST( list, poss, rhss );

    // push the right hand sides again
    PushObj(intr, rhss);
}

void IntrAssListLevel(IntrState * intr, Int narg, UInt level)
{
    Obj                 lists;          // lists, left operand
    Obj                 pos;            // position, left operand
    Obj                 rhss;           // right hand sides, right operand
    Obj ixs;
    Int i;

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAssListLevel(intr->cs, narg, level);
        return;
    }

    // get right hand sides (checking is done by 'AssListLevel')
    rhss = PopObj(intr);

    ixs = NEW_PLIST(T_PLIST, narg);
    for (i = narg; i > 0; i--) {
        // get and check the position
        pos = PopObj(intr);
        SET_ELM_PLIST(ixs, i, pos);
        CHANGED_BAG(ixs);
    }
    SET_LEN_PLIST(ixs, narg);

    // get lists (if this works, then <lists> is nested <level> deep,
    // checking it is nested <level>+1 deep is done by 'AssListLevel')
    lists = PopObj(intr);

    // assign the right hand sides to the elements of several lists
    AssListLevel( lists, ixs, rhss, level );

    // push the assigned values again
    PushObj(intr, rhss);
}

void IntrAsssListLevel(IntrState * intr, UInt level)
{
    Obj                 lists;          // lists, left operand
    Obj                 poss;           // position, left operand
    Obj                 rhss;           // right hand sides, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAsssListLevel(intr->cs, level);
        return;
    }


    // get right hand sides (checking is done by 'AsssListLevel')
    rhss = PopObj(intr);

    // get and check the positions
    poss = PopObj(intr);
    CheckIsPossList("List Assignments", poss);

    // get lists (if this works, then <lists> is nested <level> deep,
    // checking it is nested <level>+1 deep is done by 'AsssListLevel')
    lists = PopObj(intr);

    // assign the right hand sides to several elements of several lists
    AsssListLevel( lists, poss, rhss, level );

    // push the assigned values again
    PushObj(intr, rhss);
}

void IntrUnbList(IntrState * intr, Int narg)
{
    Obj                 list;           // list
    Obj                 pos;            // position

    GAP_ASSERT(narg == 1 || narg == 2);

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeUnbList(intr->cs, narg);
        return;
    }

    if (narg == 1) {
      // get and check the position
      pos = PopObj(intr);

      // get the list (checking is done by 'UNB_LIST' or 'UNBB_LIST')
      list = PopObj(intr);

      // unbind the element
      if (IS_POS_INTOBJ(pos)) {
        UNB_LIST( list, INT_INTOBJ(pos) );
      }
      else {
        UNBB_LIST(list, pos);
      }
    }
    else if (narg == 2) {
        Obj col = PopObj(intr);
        Obj row = PopObj(intr);
        list = PopObj(intr);

        UNB_MAT(list, row, col);
    }

    // push void
    PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrElmList() . . . . . . . . . . . . . . . interpret selection of a list
*F  IntrElmsList()  . . . . . . . . .  interpret multiple selection of a list
*F  IntrElmListLevel(<level>) . . . . .  interpret selection of several lists
*F  IntrElmsListLevel(<level>)  . .  intr multiple selection of several lists
*/

void IntrElmList(IntrState * intr, Int narg)
{
    Obj                 elm;            // element, result
    Obj                 list;           // list, left operand
    Obj                 pos;            // position, right operand

    GAP_ASSERT(narg == 1 || narg == 2);

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeElmList(intr->cs, narg);
        return;
    }

    if (narg == 1) {
      // get the position
      pos = PopObj(intr);

      // get the list (checking is done by 'ELM_LIST')
      list = PopObj(intr);

      // get the element of the list
      if (IS_POS_INTOBJ(pos)) {
        elm = ELM_LIST( list, INT_INTOBJ( pos ) );
      }
      else {
        elm = ELMB_LIST( list, pos );
      }
    }
    else /*if (narg == 2)*/ {
        Obj col = PopObj(intr);
        Obj row = PopObj(intr);
        list = PopObj(intr);

        elm = ELM_MAT(list, row, col);
    }

    // push the element
    PushObj(intr, elm);
}

void IntrElmsList(IntrState * intr)
{
    Obj                 elms;           // elements, result
    Obj                 list;           // list, left operand
    Obj                 poss;           // positions, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeElmsList(intr->cs);
        return;
    }


    // get and check the positions
    poss = PopObj(intr);
    CheckIsPossList("List Elements", poss);

    // get the list (checking is done by 'ELMS_LIST')
    list = PopObj(intr);

    // select several elements from the list
    elms = ELMS_LIST( list, poss );

    // push the elements
    PushObj(intr, elms);
}

void IntrElmListLevel(IntrState * intr, Int narg, UInt level)
{
    Obj                 lists;          // lists, left operand
    Obj                 pos;            // position, right operand
    Obj ixs;
    Int i;

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeElmListLevel(intr->cs, narg, level);
        return;
    }

    // get the positions
    ixs = NEW_PLIST(T_PLIST, narg);
    for (i = narg; i > 0; i--) {
        pos = PopObj(intr);
        SET_ELM_PLIST(ixs, i, pos);
        CHANGED_BAG(ixs);
    }
    SET_LEN_PLIST(ixs, narg);

    // get lists (if this works, then <lists> is nested <level> deep,
    // checking it is nested <level>+1 deep is done by 'ElmListLevel')
    lists = PopObj(intr);

    // select the elements from several lists (store them in <lists>)
    ElmListLevel( lists, ixs, level );

    // push the elements
    PushObj(intr, lists);
}

void IntrElmsListLevel(IntrState * intr, UInt level)
{
    Obj                 lists;          // lists, left operand
    Obj                 poss;           // positions, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeElmsListLevel(intr->cs, level);
        return;
    }


    // get and check the positions
    poss = PopObj(intr);
    CheckIsPossList("List Elements", poss);

    // get lists (if this works, then <lists> is nested <level> deep,
    // checking it is nested <level>+1 deep is done by 'ElmsListLevel')
    lists = PopObj(intr);

    // select several elements from several lists (store them in <lists>)
    ElmsListLevel( lists, poss, level );

    // push the elements
    PushObj(intr, lists);
}

void IntrIsbList(IntrState * intr, Int narg)
{
    Obj                 isb;            // isbound, result
    Obj                 list;           // list, left operand
    Obj                 pos;            // position, right operand

    GAP_ASSERT(narg == 1 || narg == 2);

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIsbList(intr->cs, narg);
        return;
    }

    if (narg == 1) {
      // get and check the position
      pos = PopObj(intr);

      // get the list (checking is done by 'ISB_LIST' or 'ISBB_LIST')
      list = PopObj(intr);

      // get the result
      if (IS_POS_INTOBJ(pos)) {
        isb = ISB_LIST( list, INT_INTOBJ(pos) ) ? True : False;
      }
      else {
        isb = ISBB_LIST( list, pos ) ? True : False;
      }
    }
    else /*if (narg == 2)*/ {
        Obj col = PopObj(intr);
        Obj row = PopObj(intr);
        list = PopObj(intr);

        isb = ISB_MAT(list, row, col) ? True : False;
    }

    // push the result
    PushObj(intr, isb);
}


/****************************************************************************
**
*F  IntrAssRecName(<rnam>)  . . . . . . . .  interpret assignment to a record
*F  IntrAssRecExpr()  . . . . . . . . . . .  interpret assignment to a record
*/

void IntrAssRecName(IntrState * intr, UInt rnam)
{
    Obj                 record;         // record, left operand
    Obj                 rhs;            // rhs, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAssRecName(intr->cs, rnam);
        return;
    }


    // get the right hand side
    rhs = PopObj(intr);

    // get the record (checking is done by 'ASS_REC')
    record = PopObj(intr);

    // assign the right hand side to the element of the record
    ASS_REC( record, rnam, rhs );

    // push the assigned value
    PushObj(intr, rhs);
}

void IntrAssRecExpr(IntrState * intr)
{
    Obj                 record;         // record, left operand
    UInt                rnam;           // name, left operand
    Obj                 rhs;            // rhs, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAssRecExpr(intr->cs);
        return;
    }


    // get the right hand side
    rhs = PopObj(intr);

    // get the name and convert it to a record name
    rnam = RNamObj(PopObj(intr));

    // get the record (checking is done by 'ASS_REC')
    record = PopObj(intr);

    // assign the right hand side to the element of the record
    ASS_REC( record, rnam, rhs );

    // push the assigned value
    PushObj(intr, rhs);
}

void IntrUnbRecName(IntrState * intr, UInt rnam)
{
    Obj                 record;         // record, left operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeUnbRecName(intr->cs, rnam);
        return;
    }


    // get the record (checking is done by 'UNB_REC')
    record = PopObj(intr);

    // assign the right hand side to the element of the record
    UNB_REC( record, rnam );

    // push void
    PushVoidObj(intr);
}

void IntrUnbRecExpr(IntrState * intr)
{
    Obj                 record;         // record, left operand
    UInt                rnam;           // name, left operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeUnbRecExpr(intr->cs);
        return;
    }


    // get the name and convert it to a record name
    rnam = RNamObj(PopObj(intr));

    // get the record (checking is done by 'UNB_REC')
    record = PopObj(intr);

    // assign the right hand side to the element of the record
    UNB_REC( record, rnam );

    // push void
    PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrElmRecName(<rnam>)  . . . . . . . . . interpret selection of a record
*F  IntrElmRecExpr()  . . . . . . . . . . . . interpret selection of a record
*/

void IntrElmRecName(IntrState * intr, UInt rnam)
{
    Obj                 elm;            // element, result
    Obj                 record;         // the record, left operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeElmRecName(intr->cs, rnam);
        return;
    }


    // get the record (checking is done by 'ELM_REC')
    record = PopObj(intr);

    // select the element of the record
    elm = ELM_REC( record, rnam );

    // push the element
    PushObj(intr, elm);
}

void IntrElmRecExpr(IntrState * intr)
{
    Obj                 elm;            // element, result
    Obj                 record;         // the record, left operand
    UInt                rnam;           // the name, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeElmRecExpr(intr->cs);
        return;
    }


    // get the name and convert it to a record name
    rnam = RNamObj(PopObj(intr));

    // get the record (checking is done by 'ELM_REC')
    record = PopObj(intr);

    // select the element of the record
    elm = ELM_REC( record, rnam );

    // push the element
    PushObj(intr, elm);
}

void IntrIsbRecName(IntrState * intr, UInt rnam)
{
    Obj                 isb;            // element, result
    Obj                 record;         // the record, left operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIsbRecName(intr->cs, rnam);
        return;
    }


    // get the record (checking is done by 'ISB_REC')
    record = PopObj(intr);

    // get the result
    isb = (ISB_REC( record, rnam ) ? True : False);

    // push the result
    PushObj(intr, isb);
}

void IntrIsbRecExpr(IntrState * intr)
{
    Obj                 isb;            // element, result
    Obj                 record;         // the record, left operand
    UInt                rnam;           // the name, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIsbRecExpr(intr->cs);
        return;
    }


    // get the name and convert it to a record name
    rnam = RNamObj(PopObj(intr));

    // get the record (checking is done by 'ISB_REC')
    record = PopObj(intr);

    // get the result
    isb = (ISB_REC( record, rnam ) ? True : False);

    // push the result
    PushObj(intr, isb);
}


/****************************************************************************
**
*F  IntrAssPosObj() . . . . . . . . . . . .  interpret assignment to a posobj
*/

void IntrAssPosObj(IntrState * intr)
{
    Obj                 posobj;         // posobj
    Obj                 pos;            // position
    Int                 p;              // position, as a C integer
    Obj                 rhs;            // right hand side

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAssPosObj(intr->cs);
        return;
    }


    // get the right hand side
    rhs = PopObj(intr);

    // get and check the position
    pos = PopObj(intr);
    p = GetPositiveSmallIntEx("PosObj Assignment", pos, "<position>");

    // get the posobj (checking is done by 'AssPosObj')
    posobj = PopObj(intr);

    // assign to the element of the posobj
    AssPosObj(posobj, p, rhs);

    // push the right hand side again
    PushObj(intr, rhs);
}

void IntrUnbPosObj(IntrState * intr)
{
    Obj                 posobj;         // posobj
    Obj                 pos;            // position
    Int                 p;              // position, as a C integer

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeUnbPosObj(intr->cs);
        return;
    }


    // get and check the position
    pos = PopObj(intr);
    p = GetPositiveSmallIntEx("PosObj Assignment", pos, "<position>");

    // get the posobj (checking is done by 'UnbPosObj')
    posobj = PopObj(intr);

    // unbind the element
    UnbPosObj(posobj, p);

    // push void
    PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrElmPosObj() . . . . . . . . . . . . . interpret selection of a posobj
*/

void IntrElmPosObj(IntrState * intr)
{
    Obj                 elm;            // element, result
    Obj                 posobj;         // posobj, left operand
    Obj                 pos;            // position, right operand
    Int                 p;              // position, as C integer

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeElmPosObj(intr->cs);
        return;
    }


    // get and check the position
    pos = PopObj(intr);
    p = GetPositiveSmallIntEx("PosObj Element", pos, "<position>");

    // get the posobj (checking is done by 'ElmPosObj')
    posobj = PopObj(intr);

    // get the element of the posobj
    elm = ElmPosObj(posobj, p);

    // push the element
    PushObj(intr, elm);
}

void IntrIsbPosObj(IntrState * intr)
{
    Obj                 isb;            // isbound, result
    Obj                 posobj;         // posobj, left operand
    Obj                 pos;            // position, right operand
    Int                 p;              // position, as C integer

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIsbPosObj(intr->cs);
        return;
    }


    // get and check the position
    pos = PopObj(intr);
    p = GetPositiveSmallIntEx("PosObj Element", pos, "<position>");

    // get the posobj (checking is done by 'IsbPosObj')
    posobj = PopObj(intr);

    // get the result
    isb = IsbPosObj(posobj, p) ? True : False;

    // push the result
    PushObj(intr, isb);
}


/****************************************************************************
**
*F  IntrAssComObjName(<rnam>) . . . . . . .  interpret assignment to a comobj
*F  IntrAssComObjExpr() . . . . . . . . . .  interpret assignment to a comobj
*/

void IntrAssComObjName(IntrState * intr, UInt rnam)
{
    Obj                 comobj;         // comobj, left operand
    Obj                 rhs;            // rhs, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAssComObjName(intr->cs, rnam);
        return;
    }


    // get the right hand side
    rhs = PopObj(intr);

    // get the comobj (checking is done by 'AssComObj')
    comobj = PopObj(intr);

    // assign the right hand side to the element of the comobj
    AssComObj(comobj, rnam, rhs);

    // push the assigned value
    PushObj(intr, rhs);
}

void IntrAssComObjExpr(IntrState * intr)
{
    Obj                 comobj;         // comobj, left operand
    UInt                rnam;           // name, left operand
    Obj                 rhs;            // rhs, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAssComObjExpr(intr->cs);
        return;
    }


    // get the right hand side
    rhs = PopObj(intr);

    // get the name and convert it to a comobj name
    rnam = RNamObj(PopObj(intr));

    // get the comobj (checking is done by 'AssComObj')
    comobj = PopObj(intr);

    // assign the right hand side to the element of the comobj
    AssComObj(comobj, rnam, rhs);

    // push the assigned value
    PushObj(intr, rhs);
}

void IntrUnbComObjName(IntrState * intr, UInt rnam)
{
    Obj                 comobj;         // comobj, left operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeUnbComObjName(intr->cs, rnam);
        return;
    }


    // get the comobj (checking is done by 'UnbComObj')
    comobj = PopObj(intr);

    // unbind the element of the comobj
    UnbComObj(comobj, rnam);

    // push void
    PushVoidObj(intr);
}

void IntrUnbComObjExpr(IntrState * intr)
{
    Obj                 comobj;         // comobj, left operand
    UInt                rnam;           // name, left operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeUnbComObjExpr(intr->cs);
        return;
    }


    // get the name and convert it to a comobj name
    rnam = RNamObj(PopObj(intr));

    // get the comobj (checking is done by 'UnbComObj')
    comobj = PopObj(intr);

    // unbind the element of the comobj
    UnbComObj(comobj, rnam);

    // push void
    PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrElmComObjName(<rnam>) . . . . . . . . interpret selection of a comobj
*F  IntrElmComObjExpr() . . . . . . . . . . . interpret selection of a comobj
*/

void IntrElmComObjName(IntrState * intr, UInt rnam)
{
    Obj                 elm;            // element, result
    Obj                 comobj;         // the comobj, left operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeElmComObjName(intr->cs, rnam);
        return;
    }

    // get the comobj (checking is done by 'ElmComObj')
    comobj = PopObj(intr);

    // select the element of the comobj
    elm = ElmComObj(comobj, rnam);

    // push the element
    PushObj(intr, elm);
}

void IntrElmComObjExpr(IntrState * intr)
{
    Obj                 elm;            // element, result
    Obj                 comobj;         // the comobj, left operand
    UInt                rnam;           // the name, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeElmComObjExpr(intr->cs);
        return;
    }

    // get the name and convert it to a comobj name
    rnam = RNamObj(PopObj(intr));

    // get the comobj (checking is done by 'ElmComObj')
    comobj = PopObj(intr);

    // select the element of the comobj
    elm = ElmComObj(comobj, rnam);

    // push the element
    PushObj(intr, elm);
}

void IntrIsbComObjName(IntrState * intr, UInt rnam)
{
    Obj                 isb;            // element, result
    Obj                 comobj;         // the comobj, left operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIsbComObjName(intr->cs, rnam);
        return;
    }

    // get the comobj (checking is done by 'IsbComObj')
    comobj = PopObj(intr);

    // get the result
    isb = IsbComObj(comobj, rnam) ? True : False;

    // push the result
    PushObj(intr, isb);
}

void IntrIsbComObjExpr(IntrState * intr)
{
    Obj                 isb;            // element, result
    Obj                 comobj;         // the comobj, left operand
    UInt                rnam;           // the name, right operand

    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeIsbComObjExpr(intr->cs);
        return;
    }

    // get the name and convert it to a comobj name
    rnam = RNamObj(PopObj(intr));

    // get the comobj (checking is done by 'IsbComObj')
    comobj = PopObj(intr);

    // get the result
    isb = IsbComObj(comobj, rnam) ? True : False;

    // push the result
    PushObj(intr, isb);
}

/****************************************************************************
**
*F  IntrEmpty() . . . . . . . . . . . . .  Interpret an empty statement body
**
*/


void IntrEmpty(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeEmpty(intr->cs);
        return;
    }


    // interpret
    PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrInfoBegin() . . . . . . . . .  start interpretation of Info statement
*F  IntrInfoMiddle()  . . . . . .  shift to interpreting printable statements
*F  IntrInfoEnd( <narg> ) . . Info statement complete, <narg> things to print
*V  InfoDecision . . . . . . . . . . .  fopy of the InfoDecision GAP function
**
**  These are the actions which are used to interpret an Info statement:
**
**  IntrInfoBegin is called after the Info is read
**
**  IntrInfoMiddle is called after reading two arguments, because we can
**  now decide whether we should evaluate or ignore the remaining arguments
**
**  IntrInfoEnd is called when the closing ')' is detected and should
**  trigger the actual printing, if needed. The argument is the number of
**  things to print
*/



void IntrInfoBegin(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeInfoBegin(intr->cs);
        return;
    }
}


void IntrInfoMiddle(IntrState * intr)
{
    Obj selectors;   // first argument of Info
    Obj level;       // second argument of Info
    Obj selected;    /* GAP Boolean answer to whether this message
                        gets printed or not */


    // ignore or code
    SKIP_IF_RETURNING();
    if (intr->ignoring > 0) {
        intr->ignoring++;
        return;
    }
    if (intr->coding > 0) {
        CodeInfoMiddle(intr->cs);
        return;
    }


    level = PopObj(intr);
    selectors = PopObj(intr);

    selected = InfoCheckLevel(selectors, level);

    if (selected == False)
        intr->ignoring = 1;
    else {
        PushObj(intr, selectors);
        PushObj(intr, level);
    }
}

void IntrInfoEnd(IntrState * intr, UInt narg)
{
    Obj args; // gathers up the arguments to be printed

    // ignore or code
    INTERPRETER_PROFILE_HOOK(intr, 1);
    SKIP_IF_RETURNING_NO_PROFILE_HOOK();

    if (intr->ignoring > 1) {
        intr->ignoring--;
        return;
    }
    if (intr->coding > 0) {
        CodeInfoEnd(intr->cs, narg);
        return;
    }

    // print if necessary
    if (intr->ignoring > 0)
        intr->ignoring--;
    else {
        args = NEW_PLIST(T_PLIST, narg);
        SET_LEN_PLIST(args, narg);
        while (narg > 0)
            SET_ELM_PLIST(args, narg--, PopObj(intr));

        Obj level = PopObj(intr);
        Obj selectors = PopObj(intr);

        InfoDoPrint(selectors, level, args);
    }

    /* If we actually executed this statement at all
       (even if we printed nothing) then return a Void */

    if (intr->ignoring == 0)
        PushVoidObj(intr);
}


/****************************************************************************
**
*F  IntrAssertBegin()  . . . . . . . start interpretation of Assert statement
*F  IntrAssertAfterLevel() . .  called after the first argument has been read
**
**  At this stage, we can decide whether to evaluate the second argument --
**   the check in question
**
*F  IntrAssertAfterCondition() called after the second argument has been read
**
**  At this point we know whether there is an assertion failure. We still
**  need to read the third argument if any, to decide what to do about it;
**  one of:
**
*F  IntrAssertEnd2Args() . . . . called after reading the closing parenthesis
*F  IntrAssertEnd3Args() . . . . called after reading the closing parenthesis
**
**
**  intr->ignoring is increased by (a total of) 2 if an assertion either
**  is not tested (because we were Ignoring when we got to it, or due to
**  level) or is tested and passes
*/


void IntrAssertBegin(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    SKIP_IF_IGNORING();
    if (intr->coding > 0) {
        CodeAssertBegin(intr->cs);
        return;
    }
}


void IntrAssertAfterLevel(IntrState * intr)
{
    // ignore or code
    SKIP_IF_RETURNING();
    if (intr->ignoring > 0) {
        intr->ignoring++;
        return;
    }
    if (intr->coding > 0) {
        CodeAssertAfterLevel(intr->cs);
        return;
    }


    Int level = GetSmallIntEx("Assert", PopObj(intr), "<lev>");

    if (STATE(CurrentAssertionLevel) < level)
        intr->ignoring = 1;
}

void IntrAssertAfterCondition(IntrState * intr)
{
    Obj condition;

    // ignore or code
    SKIP_IF_RETURNING();
    if (intr->ignoring > 0) {
        intr->ignoring++;
        return;
    }
    if (intr->coding > 0) {
        CodeAssertAfterCondition(intr->cs);
        return;
    }

    condition = PopObj(intr);

    if (condition == True)
        intr->ignoring = 2;
    else if (condition != False)
        RequireArgumentEx("Assert", condition, "<cond>",
                          "must be 'true' or 'false'");
}

void IntrAssertEnd2Args(IntrState * intr)
{
    // ignore or code
    INTERPRETER_PROFILE_HOOK(intr, 2);
    SKIP_IF_RETURNING_NO_PROFILE_HOOK();
    if (intr->ignoring > 2) {
        intr->ignoring -= 2;
        return;
    }
    if (intr->coding > 0) {
        CodeAssertEnd2Args(intr->cs);
        return;
    }

    if (intr->ignoring == 0)
        AssertionFailure();
    else
        intr->ignoring -= 2;

    GAP_ASSERT(intr->ignoring == 0);
    PushVoidObj(intr);
}


void IntrAssertEnd3Args(IntrState * intr)
{
    Obj message;
    // ignore or code
    INTERPRETER_PROFILE_HOOK(intr, 2);
    SKIP_IF_RETURNING_NO_PROFILE_HOOK();
    if (intr->ignoring > 2) {
        intr->ignoring -= 2;
        return;
    }
    if (intr->coding > 0) {
        CodeAssertEnd3Args(intr->cs);
        return;
    }

    if (intr->ignoring == 0) {
        message = PopVoidObj(intr);
        AssertionFailureWithMessage(message);
    }
    else
        intr->ignoring -= 2;

    GAP_ASSERT(intr->ignoring == 0);
    PushVoidObj(intr);
}


/****************************************************************************
**
*F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
*/



/****************************************************************************
**
*F  InitKernel( <module> )  . . . . . . . . initialise kernel data structures
*/

static Int InitKernel (
    StructInitInfo *    module )
{
    // register global bags with the garbage collector
    InitGlobalBag( &STATE(ErrorLVars), "STATE(ErrorLVars)"         );
    InitGlobalBag( &STATE(Tilde), "STATE(Tilde)" );
    InitGlobalBag( &VoidReturnMarker, "VoidReturnMarker");

    // Create a bag which is not used anywhere else
    VoidReturnMarker = NEW_STRING(0);

    InitFopyGVar( "CONVERT_FLOAT_LITERAL_EAGER", &CONVERT_FLOAT_LITERAL_EAGER);

    // The work of handling options is also delegated
    ImportFuncFromLibrary( "PushOptions", &PushOptions );
    ImportFuncFromLibrary( "PopOptions",  &PopOptions  );

    return 0;
}


/****************************************************************************
**
*F  InitInfoIntrprtr()  . . . . . . . . . . . . . . . table of init functions
*/

static StructInitInfo module = {
    // init struct using C99 designated initializers; for a full list of
    // fields, please refer to the definition of StructInitInfo
    .type = MODULE_BUILTIN,
    .name = "intrprtr",
    .initKernel = InitKernel,
};

StructInitInfo * InitInfoIntrprtr(void)
{
    return &module;
}

Messung V0.5 in Prozent
C=84 H=86 G=84

¤ Dauer der Verarbeitung: 0.90 Sekunden  (vorverarbeitet am  2026-04-28) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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 und die Messung sind noch experimentell.