/**************************************************************************** ** ** 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 module contains the functions to read expressions and statements.
*/
/**************************************************************************** ** *S TRY_IF_NO_ERROR ** ** To deal with errors found by the reader, we implement a kind of exception ** handling using setjmp, with the help of this macro. See also ** GAP_TRY and GAP_CATCH in trycatch.h for two closely related macros. ** ** To use this construct, write code like this: ** TRY_IF_NO_ERROR { ** ... code which might trigger reader error ... ** } ** ** Then, if the reader encounters an error, or if the interpretation of an ** expression or statement leads to an error, 'GAP_THROW' is invoked, ** which in turn calls 'longjmp' to return to right after the block ** following TRY_IF_NO_ERROR. ** ** A second effect of 'TRY_IF_NO_ERROR' is that it prevents the execution of ** the code it wraps if 'rs->s.NrError' is non-zero, i.e. if any errors ** occurred. This is key for enabling graceful error recovery in the reader, ** and for this reason it is crucial that all calls from the reader into ** the interpreter are wrapped into 'TRY_IF_NO_ERROR' blocks. ** ** The above is the first major difference between 'TRY_IF_NO_ERROR' and the ** related GAP_TRY macro. The second is that unlike GAP_TRY / GAP_CATCH, ** the TRY_IF_NO_ERROR macro does not save and restore the jump buffer. ** Rather this is only done at the top level in 'ReadEvalCommand', for ** performance reasons (saving and restoring a jump buffer is expensive) ** ** As a result, it is not safe to nest TRY_IF_NO_ERROR constructs without ** extra precautions. So in general it is best to not invoke any code ** which again uses TRY_IF_NO_ERROR from inside a TRY_IF_NO_ERROR block. ** This is automatically ensured if one just calls interpreter functions, ** as the interpreter does not use TRY_IF_NO_ERROR.
*/
#define TRY_IF_NO_ERROR \ if (!rs->s.NrError) { \ volatileInt recursionDepth = GetRecursionDepth(); \ if (_setjmp(STATE(ReadJmpError))) { \
SetRecursionDepth(recursionDepth); \
rs->s.NrError++; \
} \
} \ if (!rs->s.NrError)
struct ReaderState {
ScannerState s;
IntrState intr;
// 'StackNams' is a stack of local variables names lists. A new names // list is pushed onto this stack when the reader begins to read a new // function expression (after reading the argument list and the local // variables list), and popped again when the reader has finished reading // the function expression (after reading the 'end').
Obj StackNams;
// 'ReadTop' is 0 if the reader is currently not reading a list or record // expression. 'ReadTop' is 1 if the reader is currently reading an // outmost list or record expression. 'ReadTop' is larger than 1 if the // reader is currently reading a nested list or record expression.
UInt ReadTop;
// 'ReadTilde' is 1 if the reader has read a reference to a '~' symbol // within the current outmost list or record expression.
UInt ReadTilde;
// 'CurrLHSGVar' is the current left hand side of an assignment. It is // used to prevent undefined global variable warnings, when reading a // recursive function.
UInt CurrLHSGVar;
// 'LoopNesting' records how many nested loops are active. Initially it // is 0 and is incremented each time we enter a loop, and decremented when // we exit one. It is used to determine whether 'break' and 'continue' // statements are valid.
UInt LoopNesting;
};
typedefstruct ReaderState ReaderState;
/**************************************************************************** ** ** The constructs <Expr> and <Statements> may have themselves as subpart, ** e.g., '<Var>( <Expr> )' is <Expr> and 'if <Expr> then <Statements> fi;' ** is <Statements>. The functions 'ReadExpr' and 'ReadStats' must therefore ** be declared forward.
*/ staticvoid ReadExpr(ReaderState * rs, TypSymbolSet follow, Char mode);
static UInt GlobalComesFromEnclosingForLoop(ReaderState * rs, UInt var)
{ for (UInt i = 0; i < rs->CurrentGlobalForLoopDepth; i++) { if (i == ARRAY_SIZE(rs->CurrentGlobalForLoopVariables)) return 0; if (rs->CurrentGlobalForLoopVariables[i] == var) return 1;
} return 0;
}
// `Match_` is a thin wrapper around the scanner's Match() function, in which // we can track the start line of each interpreter "instruction". This // information is then used for profiling. staticvoid Match_(ReaderState * rs,
UInt symbol, constChar * msg,
TypSymbolSet skipto)
{ if (rs->intr.startLine == 0 && symbol != S_ILLEGAL) {
rs->intr.startLine = rs->s.SymbolStartLine[0];
}
Match(&rs->s, symbol, msg, skipto);
}
// match either a semicolon or a dual semicolon staticvoid MatchSemicolon(ReaderState * rs, TypSymbolSet skipto)
{
Match_(rs, rs->s.Symbol == S_DUALSEMICOLON ? S_DUALSEMICOLON : S_SEMICOLON, ";", skipto);
}
// Search the plist 'nams' for a string equal to 'value' between and // including index 'start' and 'end' and return its index; return 0 if not // found. static UInt findValueInNams(Obj nams, constChar * val, UInt start, UInt end)
{
GAP_ASSERT(LEN_PLIST(nams) < MAX_FUNC_LVARS); for (UInt i = start; i <= end; i++) { if (streq(CONST_CSTR_STRING(ELM_PLIST(nams, i)), val)) { return i;
}
} // not found return 0;
}
/**************************************************************************** ** ** type must be one of the following: ** ** R_LVAR: local var with id <var> ** R_HVAR: high var with id <var> ** R_DVAR: debug var with id <var>, at nesting level <nest0> ** R_GVAR: global var with id <var> ** R_ELM_LIST: list access l[idx], uses <narg>, <level> ** R_ELMS_LIST: list access l{indices}, uses <level> ** R_ELM_POSOBJ: pos obj access obj![idx] ** R_ELM_REC_NAME: record access r.<rnam> ** R_ELM_REC_EXPR record access r.(expr) ** R_ELM_COMOBJ_NAME: com obj access obj.<rnam> ** R_ELM_COMOBJ_EXPR: com obj access obj.(expr) ** R_FUNCCALL function call without options & with <narg> arguments ** R_FUNCCALL_OPTS function call with options and with <narg> arguments
*/ enum REFTYPE {
R_INVALID,
R_LVAR,
R_HVAR,
R_DVAR,
R_GVAR,
R_ELM_LIST,
R_ELMS_LIST,
R_ELM_POSOBJ,
R_ELM_REC_NAME,
R_ELM_REC_EXPR,
R_ELM_COMOBJ_NAME,
R_ELM_COMOBJ_EXPR,
R_FUNCCALL,
R_FUNCCALL_OPTS,
};
GAP_STATIC_ASSERT(sizeof(LHSRef) <= 8, "LHSRef is too big");
/**************************************************************************** **
*/ static UInt EvalRef(ReaderState * rs, const LHSRef ref, Int needExpr)
{
TRY_IF_NO_ERROR
{ switch (ref.type) { case R_LVAR:
IntrRefLVar(&rs->intr, ref.var); break; case R_HVAR:
IntrRefHVar(&rs->intr, ref.var); break; case R_DVAR:
IntrRefDVar(&rs->intr, ref.var, ref.nest0); break; case R_GVAR:
IntrRefGVar(&rs->intr, ref.var); break; case R_ELM_LIST: if (ref.level == 0)
IntrElmList(&rs->intr, ref.narg); else
IntrElmListLevel(&rs->intr, ref.narg, ref.level); return ref.level; case R_ELMS_LIST: if (ref.level == 0)
IntrElmsList(&rs->intr); else
IntrElmsListLevel(&rs->intr, ref.level); return ref.level + 1; case R_ELM_POSOBJ:
IntrElmPosObj(&rs->intr); break; case R_ELM_REC_NAME:
IntrElmRecName(&rs->intr, ref.rnam); break; case R_ELM_REC_EXPR:
IntrElmRecExpr(&rs->intr); break; case R_ELM_COMOBJ_NAME:
IntrElmComObjName(&rs->intr, ref.rnam); break; case R_ELM_COMOBJ_EXPR:
IntrElmComObjExpr(&rs->intr); break; case R_FUNCCALL:
IntrFuncCallEnd(&rs->intr, needExpr, 0, ref.narg); break; case R_FUNCCALL_OPTS:
IntrFuncCallEnd(&rs->intr, needExpr, 1, ref.narg); break; case R_INVALID: default: // This should never be reached
Panic("Parse error in EvalRef");
}
} return 0;
}
staticvoid AssignRef(ReaderState * rs, const LHSRef ref)
{
TRY_IF_NO_ERROR
{ switch (ref.type) { case R_LVAR:
IntrAssLVar(&rs->intr, ref.var); break; case R_HVAR:
IntrAssHVar(&rs->intr, ref.var); break; case R_DVAR:
IntrAssDVar(&rs->intr, ref.var, ref.nest0); break; case R_GVAR:
IntrAssGVar(&rs->intr, ref.var); break; case R_ELM_LIST: if (ref.level == 0)
IntrAssList(&rs->intr, ref.narg); else
IntrAssListLevel(&rs->intr, ref.narg, ref.level); break; case R_ELMS_LIST: if (ref.level == 0)
IntrAsssList(&rs->intr); else
IntrAsssListLevel(&rs->intr, ref.level); break; case R_ELM_POSOBJ:
IntrAssPosObj(&rs->intr); break; case R_ELM_REC_NAME:
IntrAssRecName(&rs->intr, ref.rnam); break; case R_ELM_REC_EXPR:
IntrAssRecExpr(&rs->intr); break; case R_ELM_COMOBJ_NAME:
IntrAssComObjName(&rs->intr, ref.rnam); break; case R_ELM_COMOBJ_EXPR:
IntrAssComObjExpr(&rs->intr); break; case R_INVALID: case R_FUNCCALL: case R_FUNCCALL_OPTS: default: // This should never be reached
Panic("Parse error in AssignRef");
}
}
}
staticvoid UnbindRef(ReaderState * rs, const LHSRef ref)
{
TRY_IF_NO_ERROR
{ switch (ref.type) { case R_LVAR:
IntrUnbLVar(&rs->intr, ref.var); break; case R_HVAR:
IntrUnbHVar(&rs->intr, ref.var); break; case R_DVAR:
IntrUnbDVar(&rs->intr, ref.var, ref.nest0); break; case R_GVAR:
IntrUnbGVar(&rs->intr, ref.var); break; case R_ELM_LIST:
IntrUnbList(&rs->intr, ref.narg); break; case R_ELM_POSOBJ:
IntrUnbPosObj(&rs->intr); break; case R_ELM_REC_NAME:
IntrUnbRecName(&rs->intr, ref.rnam); break; case R_ELM_REC_EXPR:
IntrUnbRecExpr(&rs->intr); break; case R_ELM_COMOBJ_NAME:
IntrUnbComObjName(&rs->intr, ref.rnam); break; case R_ELM_COMOBJ_EXPR:
IntrUnbComObjExpr(&rs->intr); break; case R_INVALID: case R_ELMS_LIST: case R_FUNCCALL: case R_FUNCCALL_OPTS: default:
SyntaxError(&rs->s, "Illegal operand for 'Unbind'");
}
}
}
staticvoid IsBoundRef(ReaderState * rs, const LHSRef ref)
{
TRY_IF_NO_ERROR
{ switch (ref.type) { case R_LVAR:
IntrIsbLVar(&rs->intr, ref.var); break; case R_HVAR:
IntrIsbHVar(&rs->intr, ref.var); break; case R_DVAR:
IntrIsbDVar(&rs->intr, ref.var, ref.nest0); break; case R_GVAR:
IntrIsbGVar(&rs->intr, ref.var); break; case R_ELM_LIST:
IntrIsbList(&rs->intr, ref.narg); break; case R_ELM_POSOBJ:
IntrIsbPosObj(&rs->intr); break; case R_ELM_REC_NAME:
IntrIsbRecName(&rs->intr, ref.rnam); break; case R_ELM_REC_EXPR:
IntrIsbRecExpr(&rs->intr); break; case R_ELM_COMOBJ_NAME:
IntrIsbComObjName(&rs->intr, ref.rnam); break; case R_ELM_COMOBJ_EXPR:
IntrIsbComObjExpr(&rs->intr); break; case R_INVALID: case R_ELMS_LIST: case R_FUNCCALL: case R_FUNCCALL_OPTS: default:
SyntaxError(&rs->s, "Illegal operand for 'IsBound'");
}
}
}
// read one or more selectors while (IS_IN(rs->s.Symbol, S_LPAREN | S_LBRACK | S_LBRACE | S_DOT)) {
LHSRef ref = ReadSelector(rs, follow, level);
level = EvalRef(rs, ref, 1);
}
}
/**************************************************************************** ** *F ReadVar( <follow>, <mode> ) . . . . . . . . . . . read a variable ** ** 'ReadVar' reads a variable identifier. In case of an error it skips all ** symbols up to one contained in <follow>. ** ** <Ident> := a|b|..|z|A|B|..|Z { a|b|..|z|A|B|..|Z|0|..|9|_ }
*/ static LHSRef ReadVar(ReaderState * rs, TypSymbolSet follow)
{
LHSRef ref = { R_INVALID, 0, {0}, {0} };
Obj nams; // list of names of local vars.
Obj lvars; // environment
UInt nest; // nesting level of a higher var.
Obj lvars0; // environment
UInt nest0; // nesting level of a higher var.
UInt indx; // index of a local variable Char varname[MAX_VALUE_LEN]; // copy of variable name
// all variables must begin with an identifier if (rs->s.Symbol != S_IDENT) {
SyntaxError(&rs->s, "Identifier expected"); return ref;
}
// try to look up the variable on the stack of local variables const UInt countNams = LEN_PLIST(rs->StackNams); for (nest = 0; nest < countNams; nest++) { if (nest >= MAX_FUNC_EXPR_NESTING) {
Pr("Warning: abandoning search for %s at %dth higher frame\n",
(Int)rs->s.Value, MAX_FUNC_EXPR_NESTING); break;
}
nams = ELM_PLIST(rs->StackNams, countNams - nest);
indx = findValueInNams(nams, rs->s.Value, 1, LEN_PLIST(nams)); if (indx != 0) {
ref.type = (nest == 0) ? R_LVAR : R_HVAR;
ref.var = (nest << MAX_FUNC_LVARS_BITS) + indx; break;
}
}
// try to look up the variable on the error stack; // the outer loop runs up the calling stack, while the inner loop runs // up the static definition stack for each call function
lvars0 = STATE(ErrorLVars);
nest0 = 0; while (ref.type == R_INVALID && lvars0 != 0 && !IsBottomLVars(lvars0)) {
lvars = lvars0;
nest = 0; while (ref.type == R_INVALID && lvars != 0 && !IsBottomLVars(lvars)) {
nams = NAMS_FUNC(FUNC_LVARS(lvars)); if (nams != 0) {
indx = findValueInNams(nams, rs->s.Value, 1, LEN_PLIST(nams)); if (indx) {
ref.type = R_DVAR;
ref.var = (nest << MAX_FUNC_LVARS_BITS) + indx;
ref.nest0 = nest0; break;
}
}
lvars = ENVI_FUNC(FUNC_LVARS(lvars));
nest++; if (nest >= MAX_FUNC_EXPR_NESTING) {
Pr("Warning: abandoning search for %s at %dth higher " "frame\n",
(Int)rs->s.Value, MAX_FUNC_EXPR_NESTING); break;
}
}
lvars0 = PARENT_LVARS(lvars0);
nest0++;
}
// get the variable as a global variable if (ref.type == R_INVALID) {
ref.type = R_GVAR; // we do not want to call GVarName on this value until after we // have checked if this is the argument to a lambda function
gap_strlcpy(varname, rs->s.Value, sizeof(varname));
}
// match away the identifier, now that we know the variable
Match_(rs, S_IDENT, "identifier", follow);
// If this isn't a lambda function, look up the name if (rs->s.Symbol != S_MAPTO && ref.type == R_GVAR) {
ref.var = GVarName(varname);
}
return ref;
}
// Helper function to be called after `ReadVar`, before any further tokens // have been consumed via calls to `Match`. staticvoid CheckUnboundGlobal(ReaderState * rs, LHSRef ref)
{ // only warn if we are accessing a global variable if (ref.type != R_GVAR) return;
// only warn if inside a function if (LEN_PLIST(rs->StackNams) == 0) return;
// allow use in left hand side (LHS) of an assignment if (ref.var == rs->CurrLHSGVar) return;
// only warn if the global variable does not exist ... if (ValGVar(ref.var) != 0) return;
// ... and isn't an auto var ... if (ExprGVar(ref.var) != 0) return;
// ... and was not "declared" via DeclareGlobalName if (IsDeclaredGVar(ref.var)) return;
// don't warn if we are skipping/ignoring code if (rs->intr.ignoring) return;
// if the global was used as loop variable in an enclosing for loop, that // means it will be assigned before execution gets here, so don't warn. if (GlobalComesFromEnclosingForLoop(rs, ref.var)) return;
// check if the user disabled this warning if (WarnOnUnboundGlobalsRNam == 0)
WarnOnUnboundGlobalsRNam = RNamName("WarnOnUnboundGlobals"); if (GAPInfo && IS_REC(GAPInfo) &&
ISB_REC(GAPInfo, WarnOnUnboundGlobalsRNam) &&
ELM_REC(GAPInfo, WarnOnUnboundGlobalsRNam) == False) return;
// don't warn if we are compiling code if (SyCompilePlease) return;
// Need to pass an offset, because we have already parsed more tokens
SyntaxWarningWithOffset(&rs->s, "Unbound global variable", 2);
}
/**************************************************************************** ** *F ReadCallVarAss( <follow>, <mode> ) . . . . . . . . . . . read a variable ** ** 'ReadCallVarAss' reads a variable. In case of an error it skips all ** symbols up to one contained in <follow>. The <mode> must be one of the ** following: ** ** 'i': check if variable, record component, list entry is bound ** 'r': reference to a variable ** 's': assignment via ':=' ** 'u': unbind a variable ** 'x': either 'r' or 's' depending on <Symbol> ** ** <Ident> := a|b|..|z|A|B|..|Z { a|b|..|z|A|B|..|Z|0|..|9|_ } ** ** <Var> := <Ident> ** | <Var> '[' <Expr> [,<Expr>]* ']' ** | <Var> '{' <Expr> '}' ** | <Var> '.' <Ident> ** | <Var> '(' [ <Expr> { ',' <Expr> } ] [':' [ <options> ]] ')'
*/ staticvoid ReadCallVarAss(ReaderState * rs, TypSymbolSet follow, Char mode)
{ volatile LHSRef ref = ReadVar(rs, follow); if (ref.type == R_INVALID) return;
// if this was actually the beginning of a function literal, then we are // in the wrong function if (rs->s.Symbol == S_MAPTO) { if (mode == 'r' || mode == 'x')
ReadFuncExprAbbrevSingle(rs, follow); else
SyntaxError(&rs->s, "Function literal in impossible context"); return;
}
// Check if the variable is a constant if (ref.type == R_GVAR && IsConstantGVar(ref.var) && ValGVar(ref.var)) { // deal with references if (mode == 'r' || (mode == 'x' && rs->s.Symbol != S_ASSIGN)) {
Obj val = ValAutoGVar(ref.var);
TRY_IF_NO_ERROR { if (val == True) {
IntrTrueExpr(&rs->intr); return;
} elseif (val == False) {
IntrFalseExpr(&rs->intr); return;
} elseif (IS_INTOBJ(val)) {
IntrIntObjExpr(&rs->intr, val); return;
}
}
}
}
// check whether this is an unbound global variable if (mode != 'i') // Not inside 'IsBound'
CheckUnboundGlobal(rs, ref);
// followed by one or more selectors while (IS_IN(rs->s.Symbol, S_LPAREN | S_LBRACK | S_LBRACE | S_DOT)) {
// so the prefix was a reference
UInt level = EvalRef(rs, ref, 1);
ref = ReadSelector(rs, follow, level);
}
// if we need a reference if (mode == 'r' || (mode == 'x' && rs->s.Symbol != S_ASSIGN)) { Int needExpr = mode == 'r' || !IS_IN(rs->s.Symbol, S_SEMICOLON);
EvalRef(rs, ref, needExpr);
}
// if we need a statement elseif (mode == 's' || (mode == 'x' && rs->s.Symbol == S_ASSIGN)) { if (ref.type == R_FUNCCALL || ref.type == R_FUNCCALL_OPTS) {
TRY_IF_NO_ERROR {
IntrFuncCallEnd(&rs->intr, 0, ref.type == R_FUNCCALL_OPTS, ref.narg);
}
} else {
Match_(rs, S_ASSIGN, "found an expression when a statement was", follow);
UInt currLHSGVar = rs->CurrLHSGVar; if ( LEN_PLIST(rs->StackNams) == 0 || !rs->intr.coding ) {
rs->CurrLHSGVar = (ref.type == R_GVAR ? ref.var : 0);
}
ReadExpr(rs, follow, 'r');
AssignRef(rs, ref);
rs->CurrLHSGVar = currLHSGVar;
}
}
// if we need an unbind elseif ( mode == 'u' ) { if (rs->s.Symbol != S_RPAREN) {
SyntaxError(&rs->s, "'Unbind': argument should be followed by ')'");
}
UnbindRef(rs, ref);
}
// if we need an isbound else/* if ( mode == 'i' ) */ {
IsBoundRef(rs, ref);
}
}
/**************************************************************************** ** *F ReadIsBound( <follow> ) . . . . . . . . . . . read an isbound expression ** ** 'ReadIsBound' reads an isbound expression. In case of an error it skips ** all symbols up to one contained in <follow>. ** ** <Atom> := 'IsBound' '(' <Var> ')'
*/ staticvoid ReadIsBound(ReaderState * rs, TypSymbolSet follow)
{
Match_(rs, S_ISBOUND, "IsBound", follow);
Match_(rs, S_LPAREN, "(", follow);
ReadCallVarAss(rs, S_RPAREN|follow, 'i');
Match_(rs, S_RPAREN, ")", follow);
}
/**************************************************************************** ** *F ReadPerm( <follow> ) . . . . . . . . . . . . . . . . read a permutation ** ** 'ReadPerm' reads a permutation. In case of an error it skips all symbols ** up to one contained in <follow>. ** ** Note that the first expression has already been read. The reason is that ** until the first expression has been read and a comma is found it could ** also be a parenthesized expression. ** ** <Perm> := ( <Expr> {, <Expr>} ) { ( <Expr> {, <Expr>} ) } **
*/ staticvoid ReadPerm(ReaderState * rs, TypSymbolSet follow)
{ volatile UInt nrc; // number of cycles volatile UInt nrx; // number of expressions in cycle
// read the first cycle (first expression has already been read)
nrx = 1; while (rs->s.Symbol == S_COMMA) {
Match_(rs, S_COMMA, ",", follow);
ReadExpr(rs, S_COMMA|S_RPAREN|follow, 'r');
nrx++;
}
Match_(rs, S_RPAREN, ")", follow);
nrc = 1;
TRY_IF_NO_ERROR { IntrPermCycle(&rs->intr, nrx, nrc ); }
// that was the permutation
TRY_IF_NO_ERROR { IntrPerm(&rs->intr, nrc ); }
}
/**************************************************************************** ** *F ReadListExpr( <follow> ) . . . . . . . . . . . . . . . . . . read a list ** ** 'ReadListExpr' reads a list literal expression. In case of an error it ** skips all symbols up to one contained in <follow>. ** ** <List> := '[' [ <Expr> ] {',' [ <Expr> ] } ']' ** | '[' <Expr> [',' <Expr>] '..' <Expr> ']'
*/ staticvoid ReadListExpr(ReaderState * rs, TypSymbolSet follow)
{ volatile UInt pos; // actual position of element volatile UInt nr; // number of elements volatile UInt range; // is the list expression a range
/**************************************************************************** ** *F ReadRecExpr( <follow> ) . . . . . . . . . . . . . . . . . . read a record ** ** 'ReadRecExpr' reads a record literal expression. In case of an error it ** skips all symbols up to one contained in <follow>. ** ** <Record> := 'rec( [ <Ident>:=<Expr> {, <Ident>:=<Expr> } ] )'
*/ staticvoid ReadRecExpr(ReaderState * rs, TypSymbolSet follow)
{ volatile UInt rnam; // record component name volatile UInt nr; // number of components
/**************************************************************************** ** ** ArgList represents the return value of ReadFuncArgList
*/ typedefstruct { Int narg; // number of arguments
Obj nams; // list of local variables names BOOL isvarg; // does function have varargs? #ifdef HPCGAP
Obj locks; // locks of the function (HPC-GAP) #endif
} ArgList;
/**************************************************************************** ** *F ReadFuncArgList(<follow>, <isAtomic>, <symbol>, <symbolstr>) ** . . . . . . . . . . read a function argument list. ** ** 'ReadFuncArgList' reads the argument list of a function. In case of an ** error it skips all symbols up to one contained in <follow>. ** ** <ArgList> := ('readwrite'|'readonly') <Ident> ** {',' ('readwrite'|'readonly') <Ident> } ( '...' ) ** ** isAtomic: Is this an atomic function? ** symbol: The end symbol of the arglist (usually S_RPAREN, but S_RBRACE ** for lambda functions). ** symbolstr: symbol as an ascii string ** ** This function assumes the opening parenthesis or brace is already read, ** and is responsible for reading the closing parenthesis or brace.
*/
static ArgList ReadFuncArgList(ReaderState * rs,
TypSymbolSet follow, BOOL isAtomic,
UInt symbol, constChar * symbolstr)
{ Int narg; // number of arguments
Obj nams; // list of local variables names #ifdef HPCGAP
LockQual lockqual;
Bag locks = 0; // locks of the function #endif BOOL isvarg = FALSE; // does function have varargs?
#ifdef HPCGAP if (isAtomic)
locks = NEW_STRING(4); #endif
// make and push the new local variables list (args and locals)
narg = 0;
nams = NEW_PLIST(T_PLIST, 0); if (rs->s.Symbol != symbol) { goto start;
}
while (rs->s.Symbol == S_COMMA) { if (isvarg) {
SyntaxError(&rs->s, "Only final argument can be variadic");
}
Match_(rs, S_COMMA, ",", follow);
start: #ifdef HPCGAP
lockqual = LOCK_QUAL_NONE; #endif if (rs->s.Symbol == S_READWRITE) { if (!isAtomic) {
SyntaxError(&rs->s, "'readwrite' argument of non-atomic function");
} #ifdef HPCGAP else {
lockqual = LOCK_QUAL_READWRITE;
} #endif
Match_(rs, S_READWRITE, "readwrite", follow);
} elseif (rs->s.Symbol == S_READONLY) { if (!isAtomic) {
SyntaxError(&rs->s, "'readonly' argument of non-atomic function");
} #ifdef HPCGAP else {
lockqual = LOCK_QUAL_READONLY;
} #endif
Match_(rs, S_READONLY, "readonly", follow);
} if (rs->s.Symbol == S_IDENT && findValueInNams(nams, rs->s.Value, 1, narg)) {
SyntaxError(&rs->s, "Name used for two arguments");
}
narg += 1;
PushPlist(nams, MakeImmString(rs->s.Value)); #ifdef HPCGAP if (isAtomic) {
GrowString(locks, narg);
SET_LEN_STRING(locks, narg);
CHARS_STRING(locks)[narg - 1] = lockqual;
} #endif if (LEN_PLIST(nams) >= MAX_FUNC_LVARS) {
SyntaxError(&rs->s, "Too many function arguments");
}
Match_(rs, S_IDENT,"identifier",symbol|S_LOCAL|STATBEGIN|S_END|follow); if (rs->s.Symbol == S_DOTDOT) {
SyntaxError(&rs->s, "Three dots required for variadic argument list");
} if (rs->s.Symbol == S_DOTDOTDOT) {
isvarg = TRUE;
Match_(rs, S_DOTDOTDOT, "...", follow);
}
}
Match_(rs, symbol, symbolstr, S_LOCAL|STATBEGIN|S_END|follow);
// Special case for function(arg) if (narg == 1 && streq("arg", CONST_CSTR_STRING(ELM_PLIST(nams, narg)))) {
isvarg = TRUE;
}
while (1) { if (rs->s.Symbol == S_IDENT) { if (findValueInNams(nams, rs->s.Value, narg + 1, narg + nloc)) {
SyntaxError(&rs->s, "Name used for two locals");
} if (findValueInNams(nams, rs->s.Value, 1, narg)) {
SyntaxError(&rs->s, "Name used for argument and local");
}
nloc += 1;
PushPlist(nams, MakeImmString(rs->s.Value)); if (LEN_PLIST(nams) >= MAX_FUNC_LVARS) {
SyntaxError(&rs->s, "Too many function arguments and locals");
}
}
Match_(rs, S_IDENT, "identifier", STATBEGIN | S_END | follow);
if (rs->s.Symbol != S_COMMA) break;
// init to avoid strange message in case of empty string
rs->s.Value[0] = '\0';
Match_(rs, S_COMMA, ",", follow);
}
MatchSemicolon(rs, STATBEGIN | S_END | follow);
return nloc;
}
/**************************************************************************** ** *F ReadFuncExpr( <follow> ) . . . . . . . . . . read a function definition ** ** 'ReadFuncExpr' reads a function literal expression. In case of an error ** it skips all symbols up to one contained in <follow>. ** ** <Function> := 'function (' <ArgList> ')' ** [ 'local' <Ident> {',' <Ident>} ';' ] ** <Statements> ** 'end'
*/ staticvoid ReadFuncExpr(ReaderState * rs, TypSymbolSet follow, Char mode)
{ Int startLine; // line number of function keyword BOOL isAtomic = FALSE; // is this an atomic function?
UInt nloc = 0; // number of locals
ArgList args;
// begin the function
startLine = GetInputLineNumber(rs->s.input); if (rs->s.Symbol == S_ATOMIC) {
Match_(rs, S_ATOMIC, "atomic", follow);
isAtomic = TRUE;
} elseif (mode == 'a') { // in this case the atomic keyword was matched away by ReadAtomic // before we realised we were reading an atomic function
isAtomic = TRUE;
}
Match_(rs, S_FUNCTION, "function", follow);
Match_(rs, S_LPAREN, "(", S_IDENT|S_RPAREN|S_LOCAL|STATBEGIN|S_END|follow);
// 'end'
Match_(rs, S_END, "while parsing a function: statement or 'end'", follow);
}
/**************************************************************************** ** *F ReadFuncExprAbbrevMulti(<follow>) . . read multi-arg abbrev. func. expr. ** ** 'ReadFuncExprAbbrevMulti' reads a multi-argument abbreviated function ** literal expression. In case of an error it skips all symbols up to one ** contained in <follow>. ** ** <Function> := '{' <ArgList> '}' '->' <Expr>
*/ staticvoid ReadFuncExprAbbrevMulti(ReaderState * rs, TypSymbolSet follow)
{
Match_(rs, S_LBRACE, "{", follow);
/**************************************************************************** ** *F ReadFuncExprAbbrevSingle(<follow>) . read single-arg abbrev. func. expr. ** ** 'ReadFuncExprAbbrevSingle' reads a single-argument abbreviated function ** literal expression. In case of an error it skips all symbols up to one ** contained in <follow>. ** ** <Function> := <Var> '->' <Expr>
*/ staticvoid ReadFuncExprAbbrevSingle(ReaderState * rs, TypSymbolSet follow)
{ // make and push the new local variables list
Obj nams = NEW_PLIST(T_PLIST, 1);
PushPlist(nams, MakeImmString(rs->s.Value));
/**************************************************************************** ** *F ReadLiteral( <follow>, <mode> ) . . . . . . . . . . . . . . read an atom ** ** 'ReadLiteral' reads a literal expression. In case of an error it skips ** all symbols up to one contained in <follow>. ** ** <Literal> := <Int> ** | <Float> ** | 'true' ** | 'false' ** | '~' ** | <Char> ** | <Perm> ** | <String> ** | <List> ** | <Record> ** | <Function> ** ** <Int> := 0|1|..|9 { 0|1|..|9 } ** ** <Char> := ' <any character> ' ** ** <String> := " { <any character> } "
*/ staticvoid ReadLiteral(ReaderState * rs, TypSymbolSet follow, Char mode)
{ if (rs->s.Symbol == S_DOT) { // HACK: The only way a dot could turn up here is in a floating point // literal that starts with '.'. Call back to the scanner to deal // with this.
ScanForFloatAfterDotHACK(&rs->s);
}
/**************************************************************************** ** *F ReadQualifiedExpr( <follow>, <mode> ) . . . . . read an expression which ** may be qualified with readonly or readwrite ** ** 'ReadQualifiedExpr' reads a qualified expression. In case of an error it ** skips all symbols up to one contained in <follow>. ** ** <QualifiedExpr> := ['readonly' | 'readwrite' ] <Expr> ** ** These functions only do something meaningful inside HPC-GAP; in plain GAP ** they are simply placeholders.
*/ staticvoid
ReadQualifiedExpr(ReaderState * rs, TypSymbolSet follow, Char mode)
{ #ifdef HPCGAP volatile LockQual qual = LOCK_QUAL_NONE; #else volatile UInt qual = 0; #endif if (rs->s.Symbol == S_READWRITE) {
Match_(rs, S_READWRITE, "readwrite", follow | EXPRBEGIN); #ifdef HPCGAP
qual = LOCK_QUAL_READWRITE; #endif
} elseif (rs->s.Symbol == S_READONLY) {
Match_(rs, S_READONLY, "readonly", follow | EXPRBEGIN); #ifdef HPCGAP
qual = LOCK_QUAL_READONLY; #endif
}
TRY_IF_NO_ERROR { IntrQualifiedExprBegin(&rs->intr, qual); }
ReadExpr(rs, follow, mode);
TRY_IF_NO_ERROR { IntrQualifiedExprEnd(&rs->intr); }
}
/**************************************************************************** ** *F ReadExpr( <follow>, <mode> ) . . . . . . . . . . . . read an expression ** ** 'ReadExpr' reads an expression. In case of an error it skips all symbols ** up to one contained in <follow>. ** ** <Expr> := <And> { 'or' <And> } ** ** The <mode> is either 'r' indicating that the expression should be ** evaluated as usual, 'x' indicating that it may be the left-hand-side of ** an assignment or 'a' indicating that it is a function expression ** following an "atomic" keyword and that the function should be made ** atomic. ** ** This last case exists because when reading "atomic function" in statement ** context the atomic has been matched away before we can see that it is an ** atomic function literal, not an atomic statement. ** **
*/ staticvoid ReadExpr(ReaderState * rs, TypSymbolSet follow, Char mode)
{ // <And>
ReadAnd(rs, follow, mode);
/**************************************************************************** ** *F ReadInfo( <follow> ) . . . . . . . . . . . . . . . read an info statement ** ** 'ReadInfo' reads an info statement. In case of an error it skips all ** symbols up to one contained in <follow>. ** ** <Statement> := 'Info' '(' <Expr> ',' <Expr> { ',' <Expr> } ')' ';'
*/ staticvoid ReadInfo(ReaderState * rs, TypSymbolSet follow)
{ volatile UInt narg; // number of arguments to print (or not)
/**************************************************************************** ** *F ReadIf( <follow> ) . . . . . . . . . . . . . . . . read an if statement ** ** 'ReadIf' reads an if-statement. In case of an error it skips all symbols ** up to one contained in <follow>. ** ** <Statement> := 'if' <Expr> 'then' <Statements> ** { 'elif' <Expr> 'then' <Statements> } ** [ 'else' <Statements> ] ** 'fi' ';'
*/ staticvoid ReadIf(ReaderState * rs, TypSymbolSet follow)
{ volatile UInt nrb; // number of branches volatile UInt nrs; // number of statements in a body
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.