mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-04-14 10:07:39 +08:00
git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@4200 7fd9a85b-ad96-42d3-883c-3090e2eb8679
1684 lines
50 KiB
C
1684 lines
50 KiB
C
/****************************************************************************
|
|
* pstm.c
|
|
* Pascal Statements
|
|
*
|
|
* Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
|
|
#include "keywords.h"
|
|
#include "pasdefs.h"
|
|
#include "ptdefs.h"
|
|
#include "podefs.h"
|
|
#include "pedefs.h"
|
|
#include "pxdefs.h"
|
|
|
|
#include "pas.h"
|
|
#include "pstm.h"
|
|
#include "pproc.h"
|
|
#include "pexpr.h"
|
|
#include "pgen.h"
|
|
#include "ptkn.h"
|
|
#include "ptbl.h"
|
|
#include "pinsn.h"
|
|
#include "perr.h"
|
|
|
|
/****************************************************************************
|
|
* Private Definitions
|
|
****************************************************************************/
|
|
|
|
#define ADDRESS_DEREFERENCE 0x01
|
|
#define ADDRESS_ASSIGNMENT 0x02
|
|
#define INDEXED_ASSIGNMENT 0x04
|
|
#define VAR_PARM_ASSIGNMENT 0x08
|
|
|
|
#define isConstant(x) \
|
|
( ((x) == tINT_CONST) \
|
|
|| ((x) == tBOOLEAN_CONST) \
|
|
|| ((x) == tCHAR_CONST) \
|
|
|| ((x) == tREAL_CONST) \
|
|
|| ((x) == sSCALAR_OBJECT))
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Assignment Statements */
|
|
|
|
static void pas_ComplexAssignment(void);
|
|
static void pas_SimpleAssignment (STYPE *varPtr, uint8_t assignFlags);
|
|
static void pas_Assignment (uint16_t storeOp, exprType assignType, STYPE *varPtr, STYPE *typePtr);
|
|
static void pas_StringAssignment (STYPE *varPtr, STYPE *typePtr);
|
|
static void pas_LargeAssignment (uint16_t storeOp, exprType assignType, STYPE *varPtr, STYPE *typePtr);
|
|
|
|
/* Other Statements */
|
|
|
|
static void pas_GotoStatement (void); /* GOTO statement */
|
|
static void pas_LabelStatement (void); /* Label statement */
|
|
static void pas_ProcStatement (void); /* Procedure method statement */
|
|
static void pas_IfStatement (void); /* IF-THEN[-ELSE] statement */
|
|
static void pas_CaseStatement (void); /* Case statement */
|
|
static void pas_RepeatStatement (void); /* Repeat statement */
|
|
static void pas_WhileStatement (void); /* While statement */
|
|
static void pas_ForStatement (void); /* For statement */
|
|
static void pas_WithStatement (void); /* With statement */
|
|
|
|
/****************************************************************************/
|
|
|
|
void statement(void)
|
|
{
|
|
STYPE *symPtr; /* Save Symbol Table pointer to token */
|
|
|
|
TRACE(lstFile,"[statement");
|
|
|
|
/* Generate file/line number pseudo-operation to facilitate P-Code testing */
|
|
|
|
pas_GenerateLineNumber(FP->include, FP->line);
|
|
|
|
/* We will push the string stack pointer at the beginning of each
|
|
* statement and pop the string stack pointer at the end of each
|
|
* statement. Subsequent optimization logic will scan the generated
|
|
* pcode to ascertain if the push and pops were necessary. They
|
|
* would be necessary if expression parsing generated temporary usage
|
|
* of string stack storage. In this case, the push will save the
|
|
* value before the temporary usage and the pop will release the
|
|
* temporaray storage.
|
|
*/
|
|
|
|
pas_GenerateSimple(opPUSHS);
|
|
|
|
/* Process the statement according to the type of the leading token */
|
|
|
|
switch (token)
|
|
{
|
|
/* Simple assignment statements */
|
|
|
|
case sINT :
|
|
symPtr = tknPtr;
|
|
getToken();
|
|
pas_Assignment(opSTS, exprInteger, symPtr, symPtr->sParm.v.parent);
|
|
break;
|
|
case sCHAR :
|
|
symPtr = tknPtr;
|
|
getToken();
|
|
pas_Assignment(opSTSB, exprChar, symPtr, symPtr->sParm.v.parent);
|
|
break;
|
|
case sBOOLEAN :
|
|
symPtr = tknPtr;
|
|
getToken();
|
|
pas_Assignment(opSTSB, exprBoolean, symPtr, NULL);
|
|
break;
|
|
case sREAL :
|
|
symPtr = tknPtr;
|
|
getToken();
|
|
pas_LargeAssignment(opSTSM, exprReal, symPtr, symPtr->sParm.v.parent);
|
|
break;
|
|
case sSCALAR :
|
|
symPtr = tknPtr;
|
|
getToken();
|
|
pas_Assignment(opSTS, exprScalar, symPtr, symPtr->sParm.v.parent);
|
|
break;
|
|
case sSET_OF :
|
|
symPtr = tknPtr;
|
|
getToken();
|
|
pas_Assignment(opSTS, exprSet, symPtr, symPtr->sParm.v.parent);
|
|
break;
|
|
case sSTRING :
|
|
case sRSTRING :
|
|
symPtr = tknPtr;
|
|
getToken();
|
|
pas_StringAssignment(symPtr, symPtr->sParm.v.parent);
|
|
break;
|
|
|
|
/* Complex assignments statements */
|
|
|
|
case sSUBRANGE :
|
|
case sRECORD :
|
|
case sRECORD_OBJECT :
|
|
case sPOINTER :
|
|
case sVAR_PARM :
|
|
case sARRAY :
|
|
pas_ComplexAssignment();
|
|
break;
|
|
|
|
/* Branch, Call and Label statements */
|
|
|
|
case sPROC : pas_ProcStatement(); break;
|
|
case tGOTO : pas_GotoStatement(); break;
|
|
case tINT_CONST : pas_LabelStatement(); break;
|
|
|
|
/* Conditional Statements */
|
|
|
|
case tIF : pas_IfStatement(); break;
|
|
case tCASE : pas_CaseStatement(); break;
|
|
|
|
/* Loop Statements */
|
|
|
|
case tREPEAT : pas_RepeatStatement(); break;
|
|
case tWHILE : pas_WhileStatement(); break;
|
|
case tFOR : pas_ForStatement(); break;
|
|
|
|
/* Other Statements */
|
|
|
|
case tBEGIN : compoundStatement(); break;
|
|
case tWITH : pas_WithStatement(); break;
|
|
|
|
/* None of the above, try standard procedures */
|
|
default : builtInProcedure(); break;
|
|
|
|
} /* end switch */
|
|
|
|
/* Generate the POPS that matches the PUSHS generated at the begining
|
|
* of this function (see comments above).
|
|
*/
|
|
|
|
pas_GenerateSimple(opPOPS);
|
|
|
|
TRACE(lstFile,"]");
|
|
|
|
} /* end statement */
|
|
|
|
/***********************************************************************/
|
|
/* Process a complex assignment statement */
|
|
|
|
static void pas_ComplexAssignment(void)
|
|
{
|
|
STYPE symbolSave;
|
|
TRACE(lstFile,"[pas_ComplexAssignment]");
|
|
|
|
/* FORM: <variable OR function identifer> := <expression>
|
|
* First, make a copy of the symbol table entry because the call to
|
|
* pas_SimpleAssignment() will modify it.
|
|
*/
|
|
|
|
symbolSave = *tknPtr;
|
|
getToken();
|
|
|
|
/* Then process the complex assignment until it is reduced to a simple
|
|
* assignment (like int, char, etc.)
|
|
*/
|
|
|
|
pas_SimpleAssignment(&symbolSave, 0);
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* Process a complex assignment (recursively) until it becomes a
|
|
* simple assignment statement
|
|
*/
|
|
|
|
static void pas_SimpleAssignment(STYPE *varPtr, uint8_t assignFlags)
|
|
{
|
|
STYPE *typePtr;
|
|
TRACE(lstFile,"[pas_SimpleAssignment]");
|
|
|
|
/* FORM: <variable OR function identifer> := <expression> */
|
|
|
|
typePtr = varPtr->sParm.v.parent;
|
|
switch (varPtr->sKind)
|
|
{
|
|
/* Check if we have reduce the complex assignment to a simple
|
|
* assignment yet
|
|
*/
|
|
|
|
case sINT :
|
|
if ((assignFlags & INDEXED_ASSIGNMENT) != 0)
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDSX, varPtr);
|
|
pas_Assignment(opSTI, exprInteger, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTSX, exprIntegerPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTSX, exprInteger, varPtr, typePtr);
|
|
} /* end if */
|
|
else
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDS, varPtr);
|
|
pas_Assignment(opSTI, exprInteger, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTS, exprIntegerPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTS, exprInteger, varPtr, typePtr);
|
|
} /* end else */
|
|
break;
|
|
case sCHAR :
|
|
if ((assignFlags & INDEXED_ASSIGNMENT) != 0)
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDSX, varPtr);
|
|
pas_Assignment(opSTIB, exprChar, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTSX, exprCharPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTSXB, exprChar, varPtr, typePtr);
|
|
} /* end if */
|
|
else
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDS, varPtr);
|
|
pas_Assignment(opSTIB, exprChar, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTS, exprCharPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTSB, exprChar, varPtr, typePtr);
|
|
} /* end else */
|
|
break;
|
|
case sBOOLEAN :
|
|
if ((assignFlags & INDEXED_ASSIGNMENT) != 0)
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDSX, varPtr);
|
|
pas_Assignment(opSTI, exprBoolean, varPtr, NULL);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTSX, exprBooleanPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTSX, exprBoolean, varPtr, NULL);
|
|
} /* end if */
|
|
else
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDS, varPtr);
|
|
pas_Assignment(opSTI, exprBoolean, varPtr, NULL);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTS, exprBooleanPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTS, exprBoolean, varPtr, NULL);
|
|
} /* end else */
|
|
break;
|
|
case sREAL :
|
|
if ((assignFlags & INDEXED_ASSIGNMENT) != 0)
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDSX, varPtr);
|
|
pas_LargeAssignment(opSTIM, exprReal, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTSX, exprRealPtr, varPtr, typePtr);
|
|
else
|
|
pas_LargeAssignment(opSTSXM, exprReal, varPtr, typePtr);
|
|
} /* end if */
|
|
else
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDS, varPtr);
|
|
pas_LargeAssignment(opSTIM, exprReal, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTS, exprRealPtr, varPtr, typePtr);
|
|
else
|
|
pas_LargeAssignment(opSTSM, exprReal, varPtr, typePtr);
|
|
} /* end else */
|
|
break;
|
|
case sSCALAR :
|
|
if ((assignFlags & INDEXED_ASSIGNMENT) != 0)
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDSX, varPtr);
|
|
pas_Assignment(opSTI, exprScalar, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTSX, exprScalarPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTSX, exprScalar, varPtr, typePtr);
|
|
} /* end if */
|
|
else
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDS, varPtr);
|
|
pas_Assignment(opSTI, exprScalar, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTS, exprScalarPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTS, exprScalar, varPtr, typePtr);
|
|
} /* end else */
|
|
break;
|
|
case sSET_OF :
|
|
if ((assignFlags & INDEXED_ASSIGNMENT) != 0)
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDSX, varPtr);
|
|
pas_Assignment(opSTI, exprSet, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTSX, exprSetPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTSX, exprSet, varPtr, typePtr);
|
|
} /* end if */
|
|
else
|
|
{
|
|
if ((assignFlags & ADDRESS_DEREFERENCE) != 0)
|
|
{
|
|
pas_GenerateStackReference(opLDS, varPtr);
|
|
pas_Assignment(opSTI, exprSet, varPtr, typePtr);
|
|
} /* end if */
|
|
else if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTS, exprSetPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTS, exprSet, varPtr, typePtr);
|
|
} /* end else */
|
|
break;
|
|
|
|
/* NOPE... recurse until it becomes a simple assignment */
|
|
|
|
case sSUBRANGE :
|
|
varPtr->sKind = typePtr->sParm.t.subType;
|
|
pas_SimpleAssignment(varPtr, assignFlags);
|
|
break;
|
|
|
|
case sRECORD :
|
|
/* FORM: <record identifier>.<field> := <expression>
|
|
* OR: <record pointer identifier> := <pointer expression>
|
|
*/
|
|
|
|
/* Check if this is a pointer to a record */
|
|
|
|
if ((assignFlags & ADDRESS_ASSIGNMENT) != 0)
|
|
{
|
|
if (token == '.') error(ePOINTERTYPE);
|
|
|
|
if ((assignFlags & INDEXED_ASSIGNMENT) != 0)
|
|
pas_Assignment(opSTSX, exprRecordPtr, varPtr, typePtr);
|
|
else
|
|
pas_Assignment(opSTS, exprRecordPtr, varPtr, typePtr);
|
|
} /* end if */
|
|
else if (((assignFlags & ADDRESS_DEREFERENCE) != 0) &&
|
|
((assignFlags & VAR_PARM_ASSIGNMENT) == 0))
|
|
error(ePOINTERTYPE);
|
|
|
|
/* Check if a period separates the RECORD identifier from the
|
|
* record field identifier
|
|
*/
|
|
|
|
else if (token == '.')
|
|
{
|
|
/* Skip over the period */
|
|
|
|
getToken();
|
|
|
|
/* Verify that a field identifier associated with this record
|
|
* follows the period.
|
|
*/
|
|
|
|
if ((token != sRECORD_OBJECT) ||
|
|
(tknPtr->sParm.r.record != typePtr))
|
|
error(eRECORDOBJECT);
|
|
else
|
|
{
|
|
/* Modify the variable so that it has the characteristics of the
|
|
* the field but with level and offset associated with the record
|
|
*/
|
|
|
|
typePtr = tknPtr->sParm.r.parent;
|
|
varPtr->sKind = typePtr->sParm.t.type;
|
|
varPtr->sParm.v.parent = typePtr;
|
|
|
|
/* Special case: The record is a VAR parameter. */
|
|
|
|
if (assignFlags == (INDEXED_ASSIGNMENT | ADDRESS_DEREFERENCE | VAR_PARM_ASSIGNMENT))
|
|
{
|
|
pas_GenerateDataOperation(opPUSH, tknPtr->sParm.r.offset);
|
|
pas_GenerateSimple(opADD);
|
|
} /* end if */
|
|
else
|
|
varPtr->sParm.v.offset += tknPtr->sParm.r.offset;
|
|
|
|
getToken();
|
|
pas_SimpleAssignment(varPtr, assignFlags);
|
|
|
|
} /* end else if */
|
|
} /* end else */
|
|
|
|
/* It must be a RECORD assignment */
|
|
|
|
else
|
|
{
|
|
/* Special case: The record is a VAR parameter. */
|
|
|
|
if (assignFlags == (INDEXED_ASSIGNMENT | ADDRESS_DEREFERENCE | VAR_PARM_ASSIGNMENT))
|
|
{
|
|
pas_GenerateStackReference(opLDS, varPtr);
|
|
pas_GenerateSimple(opADD);
|
|
pas_LargeAssignment(opSTIM, exprRecord, varPtr, typePtr);
|
|
} /* end if */
|
|
else
|
|
pas_LargeAssignment(opSTSM, exprRecord, varPtr, typePtr);
|
|
} /* end else */
|
|
break;
|
|
|
|
case sRECORD_OBJECT :
|
|
/* FORM: <field> := <expression>
|
|
* NOTE: This must have been preceeded with a WITH statement
|
|
* defining the RECORD type
|
|
*/
|
|
|
|
if (!withRecord.parent)
|
|
error(eINVTYPE);
|
|
else if ((assignFlags && (ADDRESS_DEREFERENCE | ADDRESS_ASSIGNMENT)) != 0)
|
|
error(ePOINTERTYPE);
|
|
else if ((assignFlags && INDEXED_ASSIGNMENT) != 0)
|
|
error(eARRAYTYPE);
|
|
|
|
/* Verify that a field identifier is associated with the RECORD
|
|
* specified by the WITH statement.
|
|
*/
|
|
|
|
else if (varPtr->sParm.r.record != withRecord.parent)
|
|
error(eRECORDOBJECT);
|
|
|
|
else
|
|
{
|
|
int16_t tempOffset;
|
|
|
|
/* Now there are two cases to consider: (1) the withRecord is a
|
|
* pointer to a RECORD, or (2) the withRecord is the RECORD itself
|
|
*/
|
|
|
|
if (withRecord.pointer)
|
|
{
|
|
/* If the pointer is really a VAR parameter, then other syntax
|
|
* rules will apply
|
|
*/
|
|
|
|
if (withRecord.varParm)
|
|
assignFlags |= (INDEXED_ASSIGNMENT | ADDRESS_DEREFERENCE | VAR_PARM_ASSIGNMENT);
|
|
else
|
|
assignFlags |= (INDEXED_ASSIGNMENT | ADDRESS_DEREFERENCE);
|
|
|
|
pas_GenerateDataOperation(opPUSH, (varPtr->sParm.r.offset + withRecord.index));
|
|
tempOffset = withRecord.offset;
|
|
} /* end if */
|
|
else
|
|
{
|
|
tempOffset = varPtr->sParm.r.offset + withRecord.offset;
|
|
} /* end else */
|
|
|
|
/* Modify the variable so that it has the characteristics of the
|
|
* the field but with level and offset associated with the record
|
|
* NOTE: We have to be careful here because the structure
|
|
* associated with sRECORD_OBJECT is not the same as for
|
|
* variables!
|
|
*/
|
|
|
|
typePtr = varPtr->sParm.r.parent;
|
|
|
|
varPtr->sKind = typePtr->sParm.t.type;
|
|
varPtr->sLevel = withRecord.level;
|
|
varPtr->sParm.v.size = typePtr->sParm.t.asize;
|
|
varPtr->sParm.v.offset = tempOffset;
|
|
varPtr->sParm.v.parent = typePtr;
|
|
|
|
pas_SimpleAssignment(varPtr, assignFlags);
|
|
|
|
} /* end else */
|
|
break;
|
|
|
|
case sPOINTER :
|
|
/* FORM: <pointer identifier>^ := <expression>
|
|
* OR: <pointer identifier> := <pointer expression>
|
|
*/
|
|
|
|
if (token == '^') /* value assignment? */
|
|
{
|
|
getToken();
|
|
assignFlags |= ADDRESS_DEREFERENCE;
|
|
} /* end if */
|
|
else
|
|
assignFlags |= ADDRESS_ASSIGNMENT;
|
|
|
|
varPtr->sKind = typePtr->sParm.t.type;
|
|
pas_SimpleAssignment(varPtr, assignFlags);
|
|
break;
|
|
|
|
case sVAR_PARM :
|
|
if (assignFlags != 0) error(eVARPARMTYPE);
|
|
assignFlags |= (ADDRESS_DEREFERENCE | VAR_PARM_ASSIGNMENT);
|
|
|
|
varPtr->sKind = typePtr->sParm.t.type;
|
|
pas_SimpleAssignment(varPtr, assignFlags);
|
|
break;
|
|
|
|
case sARRAY :
|
|
/* FORM: <array identifier> := <expression>
|
|
* OR: <pointer array identifier>[<index>]^ := <expression>
|
|
* OR: <pointer array identifier>[<index>] := <pointer expression>
|
|
* OR: <record array identifier>[<index>].<field identifier> := <expression>
|
|
* OR: etc., etc., etc.
|
|
*/
|
|
|
|
if (assignFlags != 0) error(eARRAYTYPE);
|
|
assignFlags |= INDEXED_ASSIGNMENT;
|
|
|
|
arrayIndex(typePtr->sParm.t.asize);
|
|
varPtr->sKind = typePtr->sParm.t.type;
|
|
varPtr->sParm.v.size = typePtr->sParm.t.asize;
|
|
pas_SimpleAssignment(varPtr, assignFlags);
|
|
break;
|
|
|
|
default :
|
|
error(eINVTYPE);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* Process simple assignment statement */
|
|
|
|
static void pas_Assignment(uint16_t storeOp, exprType assignType,
|
|
STYPE *varPtr, STYPE *typePtr)
|
|
{
|
|
TRACE(lstFile,"[pas_Assignment]");
|
|
|
|
/* FORM: <variable OR function identifer> := <expression> */
|
|
|
|
if (token != tASSIGN) error (eASSIGN);
|
|
else getToken();
|
|
|
|
expression(assignType, typePtr);
|
|
pas_GenerateStackReference(storeOp, varPtr);
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* Process the assignment to a variable length string record */
|
|
|
|
static void pas_StringAssignment(STYPE *varPtr, STYPE *typePtr)
|
|
{
|
|
exprType stringKind;
|
|
|
|
TRACE(lstFile,"[pas_StringAssignment]");
|
|
|
|
/* FORM: <variable OR function identifer> := <expression> */
|
|
|
|
/* Verify that the assignment token follows the indentifier */
|
|
|
|
if (token != tASSIGN) error (eASSIGN);
|
|
else getToken();
|
|
|
|
/* Get the expression after assignment token. We'll take any kind
|
|
* of string expression. This is a hack to handle calls to system
|
|
* functions that return exprCString pointers that must be converted
|
|
* to exprString records upon assignment.
|
|
*/
|
|
|
|
stringKind = expression(exprAnyString, typePtr);
|
|
|
|
/* Place the address of the destination string structure instance on the
|
|
* stack.
|
|
*/
|
|
|
|
pas_GenerateStackReference(opLAS, varPtr);
|
|
|
|
/* Check if this is an assignment to a global allocated string, or
|
|
* to a stack reference to an allocated string.
|
|
*/
|
|
|
|
if (varPtr->sKind == sRSTRING)
|
|
{
|
|
/* It is an assignment to a string reference --
|
|
* Generate a runtime library call to copy the destination
|
|
* string string into the pascal string instance. The particular
|
|
* runtime call will account for any necesary string type conversion.
|
|
*/
|
|
|
|
if ((stringKind == exprString) || (stringKind == exprStkString))
|
|
{
|
|
/* It is a pascal string type. Current stack representation is:
|
|
*
|
|
* TOS(0)=address of dest string reference
|
|
* TOS(1)=length of source string
|
|
* TOS(2)=pointer to source string
|
|
*/
|
|
|
|
pas_BuiltInFunctionCall(lbSTR2RSTR);
|
|
}
|
|
else if (stringKind == exprCString)
|
|
{
|
|
/* It is a 32-bit C string point. Current stack representation is:
|
|
*
|
|
* TOS(0)=address of dest string reference
|
|
* TOS(1)=MS 16-bits of 32-bit C source string pointer
|
|
* TOS(2)=LS 16-bits of 32-bit C source string pointer
|
|
*/
|
|
|
|
pas_BuiltInFunctionCall(lbCSTR2RSTR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* It is an assignment to a allocated Pascal string --
|
|
* Generate a runtime library call to copy the destination
|
|
* string string into the pascal string instance. The particular
|
|
* runtime call will account for any necesary string type conversion.
|
|
*/
|
|
|
|
if ((stringKind == exprString) || (stringKind == exprStkString))
|
|
{
|
|
/* It is a pascal string type. Current stack representation is:
|
|
*
|
|
* TOS(0)=address of dest string hdr
|
|
* TOS(1)=length of source string
|
|
* TOS(2)=pointer to source string
|
|
*/
|
|
|
|
pas_BuiltInFunctionCall(lbSTR2STR);
|
|
}
|
|
else if (stringKind == exprCString)
|
|
{
|
|
/* It is a 32-bit C string point. Current stack representation is:
|
|
*
|
|
* TOS(0)=address of dest string hdr
|
|
* TOS(1)=MS 16-bits of 32-bit C source string pointer
|
|
* TOS(2)=LS 16-bits of 32-bit C source string pointer
|
|
*/
|
|
|
|
pas_BuiltInFunctionCall(lbCSTR2STR);
|
|
}
|
|
}
|
|
|
|
/* else ... type mismatch error already reported by expression() */
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* Process a multiple word assignment statement */
|
|
|
|
static void pas_LargeAssignment(uint16_t storeOp, exprType assignType,
|
|
STYPE *varPtr, STYPE *typePtr)
|
|
{
|
|
TRACE(lstFile,"[pas_LargeAssignment]");
|
|
|
|
/* FORM: <variable OR function identifer> := <expression> */
|
|
|
|
if (token != tASSIGN) error (eASSIGN);
|
|
else getToken();
|
|
|
|
expression(assignType, typePtr);
|
|
pas_GenerateDataSize(varPtr->sParm.v.size);
|
|
pas_GenerateStackReference(storeOp, varPtr);
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
static void pas_GotoStatement(void)
|
|
{
|
|
char labelname [8]; /* Label symbol table name */
|
|
STYPE *label_ptr; /* Pointer to Label Symbol */
|
|
|
|
TRACE(lstFile,"[pas_GotoStatement]");
|
|
|
|
/* FORM: GOTO <integer> */
|
|
|
|
/* Get the token after the goto reserved word. It should be an <integer> */
|
|
|
|
getToken();
|
|
if (token != tINT_CONST)
|
|
{
|
|
/* Token following the goto is not an integer */
|
|
|
|
error(eINVLABEL);
|
|
}
|
|
else
|
|
{
|
|
/* The integer label must be non-negative */
|
|
|
|
if (tknInt < 0)
|
|
{
|
|
error(eINVLABEL);
|
|
}
|
|
else
|
|
{
|
|
/* Find and verify the symbol associated with the label */
|
|
|
|
(void)sprintf (labelname, "%ld", tknInt);
|
|
if (!(label_ptr = findSymbol(labelname)))
|
|
{
|
|
error(eUNDECLABEL);
|
|
}
|
|
else if (label_ptr->sKind != sLABEL)
|
|
{
|
|
error(eINVLABEL);
|
|
}
|
|
else
|
|
{
|
|
/* Generate the branch to the label */
|
|
|
|
pas_GenerateDataOperation(opJMP, label_ptr->sParm.l.label);
|
|
}
|
|
}
|
|
|
|
/* Get the token after the <integer> value */
|
|
|
|
getToken();
|
|
}
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
static void pas_LabelStatement(void)
|
|
{
|
|
char labelName [8]; /* Label symbol table name */
|
|
STYPE *labelPtr; /* Pointer to Label Symbol */
|
|
|
|
TRACE(lstFile,"[pas_LabelStatement]");
|
|
|
|
/* FORM: <integer> : */
|
|
|
|
/* Verify that the integer is a label name */
|
|
|
|
(void)sprintf (labelName, "%ld", tknInt);
|
|
if (!(labelPtr = findSymbol(labelName)))
|
|
{
|
|
error(eUNDECLABEL);
|
|
}
|
|
else if(labelPtr->sKind != sLABEL)
|
|
{
|
|
error(eINVLABEL);
|
|
}
|
|
|
|
/* And also verify that the label symbol has not been previously
|
|
* defined.
|
|
*/
|
|
|
|
else if(!(labelPtr->sParm.l.unDefined))
|
|
{
|
|
error(eMULTLABEL);
|
|
}
|
|
else
|
|
{
|
|
/* Generate the label and indicate that it has been defined */
|
|
|
|
pas_GenerateDataOperation(opLABEL, labelPtr->sParm.l.label);
|
|
labelPtr->sParm.l.unDefined = false;
|
|
|
|
/* We have to assume that we got here via a goto statement.
|
|
* We don't have logic in place to track changes to the level
|
|
* stack pointer (LSP) register, so we have no choice but to
|
|
* invalidate that register now.
|
|
*/
|
|
|
|
pas_InvalidateCurrentStackLevel();
|
|
}
|
|
|
|
/* Skip over the label integer */
|
|
|
|
getToken();
|
|
|
|
/* Make sure that the label is followed by a colon */
|
|
|
|
if (token != ':') error (eCOLON);
|
|
else getToken();
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
static void pas_ProcStatement(void)
|
|
{
|
|
STYPE *procPtr = tknPtr;
|
|
int size = 0;
|
|
|
|
TRACE(lstFile,"[pas_ProcStatement]");
|
|
|
|
/* FORM: procedure-method-statement =
|
|
* procedure-method-specifier [ actual-parameter-list ]
|
|
*
|
|
* Skip over the procedure-method-statement
|
|
*/
|
|
|
|
getToken();
|
|
|
|
/* Get the actual parameters (if any) associated with the procedure
|
|
* call.
|
|
*/
|
|
|
|
size = actualParameterList(procPtr);
|
|
|
|
/* Generate procedure call and stack adjustment (if required)
|
|
* Upon return from the procedure, the level stack pointer (LSP)
|
|
* may also be invalid. However, we rely on level level logic in
|
|
* pgen.c to manage this case (as well as the function call case).
|
|
*/
|
|
|
|
pas_GenerateProcedureCall(procPtr);
|
|
if (size)
|
|
{
|
|
pas_GenerateDataOperation(opINDS, -size);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
static void pas_IfStatement(void)
|
|
{
|
|
uint16_t else_label = ++label;
|
|
uint16_t endif_label = else_label;
|
|
int32_t thenLSP;
|
|
int32_t elseLSP;
|
|
|
|
TRACE(lstFile,"[pas_IfStatement]");
|
|
|
|
/* FORM: IF <expression> THEN <statement> [ELSE <statement>] */
|
|
|
|
/* Skip over the IF token */
|
|
|
|
getToken();
|
|
|
|
/* Evaluate the boolean expression */
|
|
|
|
expression(exprBoolean, NULL);
|
|
|
|
/* Make sure that the boolean expression is followed by the THEN token */
|
|
|
|
if (token != tTHEN)
|
|
error (eTHEN);
|
|
else
|
|
{
|
|
/* Skip over the THEN token */
|
|
|
|
getToken();
|
|
|
|
/* Generate a conditional branch to the "else_label." This will be a
|
|
* branch to either the ENDIF or to the ELSE location (if present).
|
|
*/
|
|
|
|
pas_GenerateDataOperation(opJEQUZ, else_label);
|
|
|
|
/* Save the value of the Level Stack Pointer (LSP) here. This will be
|
|
* the value of the LSP at the ENDIF label if there is no ELSE <statement>
|
|
* presentl. We will compare the elseLSP to the thenLSP at that point.
|
|
*/
|
|
|
|
elseLSP = pas_GetCurrentStackLevel();
|
|
|
|
/* Parse the <statment> following the THEN token */
|
|
|
|
statement();
|
|
|
|
/* Save the LSP after generating the THEN <statement>. We will compare the
|
|
* elseLSP to the thenLSP below.
|
|
*/
|
|
|
|
thenLSP = pas_GetCurrentStackLevel();
|
|
|
|
/* Check for optional ELSE <statement> */
|
|
|
|
if (token == tELSE)
|
|
{
|
|
/* Change the ENDIF label. Now instead of branching to
|
|
* the ENDIF, the logic above will branch to the ELSE
|
|
* logic generated here.
|
|
*/
|
|
|
|
endif_label = ++label;
|
|
|
|
/* Skip over the ELSE token */
|
|
|
|
getToken();
|
|
|
|
/* Generate Jump to ENDIF label after the THEN <statement> */
|
|
|
|
pas_GenerateDataOperation(opJMP, endif_label);
|
|
|
|
/* Generate the ELSE label here. This is where we will go if
|
|
* the IF <expression> evaluates to false.
|
|
*/
|
|
|
|
pas_GenerateDataOperation(opLABEL, else_label);
|
|
|
|
/* Generate the ELSE <statement> then fall through to the
|
|
* ENDIF label.
|
|
*/
|
|
|
|
statement();
|
|
|
|
/* Save the LSP after generating the ELSE <statement>. We will
|
|
* compare elseLSP to the thenLSP below.
|
|
*/
|
|
|
|
elseLSP = pas_GetCurrentStackLevel();
|
|
}
|
|
|
|
/* Generate the ENDIF label here. Note that if no ELSE <statement>
|
|
* is present, this will be the same as the else_label.
|
|
*/
|
|
|
|
pas_GenerateDataOperation(opLABEL, endif_label);
|
|
|
|
/* We can get to this location through two of three pathes: (1) through the
|
|
* THEN <statement>, (2) from the IF <expression> if no ELSE <statement>
|
|
* is present, or (3) from the ELSE <statement>. If the LSP is different
|
|
* through these two pathes, then we will have to invalidate it.
|
|
*/
|
|
|
|
if (thenLSP != elseLSP)
|
|
{
|
|
pas_InvalidateCurrentStackLevel();
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
void compoundStatement(void)
|
|
{
|
|
TRACE(lstFile,"[compoundStatement]");
|
|
|
|
/* Process statements until END encountered */
|
|
do
|
|
{
|
|
getToken();
|
|
statement();
|
|
}
|
|
while (token == ';');
|
|
|
|
/* Verify that it really was END */
|
|
|
|
if (token != tEND) error (eEND);
|
|
else getToken();
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
void pas_RepeatStatement ()
|
|
{
|
|
uint16_t rpt_label = ++label;
|
|
|
|
TRACE(lstFile,"[pas_RepeatStatement]");
|
|
|
|
/* REPEAT <statement[;statement[statement...]]> UNTIL <expression> */
|
|
|
|
/* Generate top of loop label */
|
|
|
|
pas_GenerateDataOperation(opLABEL, rpt_label);
|
|
do
|
|
{
|
|
getToken();
|
|
|
|
/* Process <statement> */
|
|
|
|
statement();
|
|
}
|
|
while (token == ';');
|
|
|
|
/* Verify UNTIL follows */
|
|
|
|
if (token != tUNTIL) error (eUNTIL);
|
|
else getToken();
|
|
|
|
/* Generate UNTIL <expression> */
|
|
|
|
expression(exprBoolean, NULL);
|
|
|
|
/* Generate conditional branch to the top of loop */
|
|
|
|
pas_GenerateDataOperation(opJEQUZ, rpt_label);
|
|
|
|
/* NOTE: The current LSP setting will be correct after the repeat
|
|
* loop because we fall through from the bottom of the loop after
|
|
* executing the body at least once.
|
|
*/
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
static void pas_WhileStatement(void)
|
|
{
|
|
uint16_t while_label = ++label; /* Top of loop label */
|
|
uint16_t endwhile_label = ++label; /* End of loop label */
|
|
uint32_t nLspChanges;
|
|
int32_t topOfLoopLSP;
|
|
bool bCheckLSP = false;
|
|
|
|
TRACE(lstFile,"[pas_WhileStatement]");
|
|
|
|
/* Generate WHILE <expression> DO <statement> */
|
|
|
|
/* Skip over WHILE token */
|
|
|
|
getToken();
|
|
|
|
/* Set top of loop label */
|
|
|
|
pas_GenerateDataOperation(opLABEL, while_label);
|
|
|
|
/* Evaluate the WHILE <expression> */
|
|
|
|
nLspChanges = pas_GetNStackLevelChanges();
|
|
expression(exprBoolean, NULL);
|
|
|
|
/* Generate a conditional jump to the end of the loop */
|
|
|
|
pas_GenerateDataOperation(opJEQUZ, endwhile_label);
|
|
|
|
/* Save the level stack pointer (LSP) at the top of the
|
|
* loop. When first executed, this value will depend on
|
|
* logic prior to the loop or on values set in the
|
|
* WHILE <expression>. On subsequent loops, this value
|
|
* may be determined by logic within the loop body or
|
|
* have to restore this value when the loop terminates.
|
|
*/
|
|
|
|
topOfLoopLSP = pas_GetCurrentStackLevel();
|
|
|
|
/* Does the WHILE <expression> logic set the LSP? */
|
|
|
|
if (nLspChanges == pas_GetNStackLevelChanges())
|
|
{
|
|
/* Yes, then the value set in the WHILE <expression>
|
|
* is the one that will be in effect at the end_while
|
|
* label.
|
|
*/
|
|
|
|
bCheckLSP = true;
|
|
}
|
|
|
|
/* Verify that the DO token follows the expression */
|
|
|
|
if (token != tDO) error(eDO);
|
|
else getToken();
|
|
|
|
/* Generate the <statement> following the DO token */
|
|
|
|
statement();
|
|
|
|
/* Generate a branch to the top of the loop */
|
|
|
|
pas_GenerateDataOperation(opJMP, while_label);
|
|
|
|
/* Set the bottom of loop label */
|
|
|
|
pas_GenerateDataOperation(opLABEL, endwhile_label);
|
|
|
|
/* We always get here from the check at the top of the loop.
|
|
* Normally this will be from the branch from the bottom of
|
|
* the loop to the top of the loop. Then from the conditional
|
|
* branch at the top of the loop to here.
|
|
*
|
|
* But, we need to allow for the special case when the body
|
|
* of the while loop never executed. The flag bCheckLSP is
|
|
* set true if the conditional expression evaluation does not
|
|
* set the LSP. In the case, the current LSP will be either
|
|
* the LSP at the top of the loop (if he body was never executed)
|
|
* or the current LSP (the body executes at least once).
|
|
*/
|
|
|
|
if (bCheckLSP)
|
|
{
|
|
if (topOfLoopLSP != pas_GetCurrentStackLevel())
|
|
{
|
|
/* In thise case, there is uncertainty in the value of the
|
|
* LSP and we must invalidate it. It will be reset to the
|
|
* correct the next time that a level stack reference is
|
|
* performed.
|
|
*/
|
|
|
|
pas_InvalidateCurrentStackLevel();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, make sure that the code generation logic knows
|
|
* the correct value of the LSP at this point.
|
|
*/
|
|
|
|
pas_SetCurrentStackLevel(topOfLoopLSP);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* This is helper function for pas_CaseStatement */
|
|
|
|
static bool pas_CheckInvalidateLSP(int32_t *pTerminalLSP)
|
|
{
|
|
/* Check the LSP after evaluating the case <statement>. */
|
|
|
|
int32_t caseLSP = pas_GetCurrentStackLevel();
|
|
if (caseLSP < 0)
|
|
{
|
|
/* If the LSP is invalid after any case <statement>, then it could
|
|
* be invalid at the end_case label as well.
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
else if (*pTerminalLSP < 0)
|
|
{
|
|
/* The value of the LSP at the end_case label has not
|
|
* yet been determined. It must be the value at the
|
|
* end of this case <statement> (or else it is invalid)
|
|
*/
|
|
|
|
*pTerminalLSP = caseLSP;
|
|
}
|
|
else if (*pTerminalLSP != caseLSP)
|
|
{
|
|
/* The value of the LSP at the end of this case <statement> is
|
|
* different from the value of the LSP at the end of some other
|
|
* case <statement>. The value of the LSP at the end_case label
|
|
* will be indeterminate and must be invalidated.
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
/* So far so good */
|
|
|
|
return false;
|
|
}
|
|
|
|
static void pas_CaseStatement(void)
|
|
{
|
|
uint16_t this_case;
|
|
uint16_t next_case = ++label;
|
|
uint16_t end_case = ++label;
|
|
int32_t terminalLSP = -1;
|
|
bool bInvalidateLSP = false;
|
|
|
|
TRACE(lstFile,"[pas_CaseStatement]");
|
|
|
|
/* Process "CASE <expression> OF" */
|
|
|
|
/* Skip over the CASE token */
|
|
|
|
getToken();
|
|
|
|
/* Evaluate the CASE <expression> */
|
|
|
|
expression(exprAnyOrdinal, NULL);
|
|
|
|
/* Verify that CASE <expression> is followed with the OF token */
|
|
|
|
if (token != tOF) error (eOF);
|
|
else getToken();
|
|
|
|
/* Loop to process each case until END encountered */
|
|
|
|
for (;;)
|
|
{
|
|
this_case = next_case;
|
|
next_case = ++label;
|
|
|
|
/* Process NON-STANDARD ELSE <statement> END */
|
|
|
|
if (token == tELSE)
|
|
{
|
|
getToken();
|
|
|
|
/* Set ELSE statement label */
|
|
|
|
pas_GenerateDataOperation(opLABEL, this_case);
|
|
|
|
/* Evaluate ELSE statement */
|
|
|
|
statement();
|
|
|
|
/* Check the LSP after evaluating the ELSE <statement>. */
|
|
|
|
if (pas_CheckInvalidateLSP(&terminalLSP))
|
|
{
|
|
/* The LSP will be invalid at the end case label. Set
|
|
* a flag so that we can handle invalidation of the LSP when
|
|
* we get to the end case label.
|
|
*/
|
|
|
|
bInvalidateLSP = true;
|
|
}
|
|
|
|
/* Verify that END follows the ELSE <statement> */
|
|
|
|
if (token != tEND) error(eEND);
|
|
else getToken();
|
|
|
|
/* Terminate FOR loop */
|
|
|
|
break;
|
|
}
|
|
|
|
/* Process "<constant>[,<constant>[,...]] : <statement>"
|
|
* NOTE: We accept any kind of constant for the case selector; there
|
|
* really should be some check to assure that the constant is of the
|
|
* same type as the expression!
|
|
*/
|
|
|
|
else
|
|
{
|
|
/* Loop for each <constant> in the case list */
|
|
|
|
for(;;)
|
|
{
|
|
/* Verify that we have a constant */
|
|
|
|
if (!isConstant(token))
|
|
{
|
|
error(eINTCONST);
|
|
break;
|
|
}
|
|
|
|
/* Generate a comparison of the CASE expression and the constant.
|
|
*
|
|
* First duplicate the value to be compared (from the CASE <expression>)
|
|
* and push the comparison value (from the <constant>:)
|
|
*/
|
|
|
|
pas_GenerateSimple(opDUP);
|
|
pas_GenerateDataOperation(opPUSH, tknInt);
|
|
|
|
/* The kind of comparison we generate depends on if we have to
|
|
* jump over other case selector comparsions to the statement
|
|
* or if we can just fall through to the statement
|
|
*/
|
|
|
|
/* Skip over the constant */
|
|
|
|
getToken();
|
|
|
|
/* If there are multiple constants, they will be separated with
|
|
* commas.
|
|
*/
|
|
|
|
if (token == ',')
|
|
{
|
|
/* Generate jump to <statement> */
|
|
|
|
pas_GenerateDataOperation(opJEQUZ, this_case);
|
|
|
|
/* Skip over comma */
|
|
|
|
getToken();
|
|
}
|
|
else
|
|
{
|
|
/* else jump to the next case */
|
|
|
|
pas_GenerateDataOperation(opJNEQZ, next_case);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Then process ... : <statement> */
|
|
|
|
/* Verify colon presence */
|
|
|
|
if (token != ':') error(eCOLON);
|
|
else getToken();
|
|
|
|
/* Set CASE label */
|
|
|
|
pas_GenerateDataOperation(opLABEL, this_case);
|
|
|
|
/* Evaluate <statement> */
|
|
|
|
statement();
|
|
|
|
/* Jump to exit CASE */
|
|
|
|
pas_GenerateDataOperation(opJMP, end_case);
|
|
|
|
/* Check the LSP after evaluating the case <statement>. */
|
|
|
|
if (pas_CheckInvalidateLSP(&terminalLSP))
|
|
{
|
|
/* If the LSP will be invalid at the end case label. Set
|
|
* a flag so that we can handle invalidation of the LSP when
|
|
* we get to the end case label.
|
|
*/
|
|
|
|
bInvalidateLSP = true;
|
|
}
|
|
}
|
|
|
|
/* Check if there are more statements. If not, verify END present */
|
|
|
|
if (token == ';')
|
|
{
|
|
getToken();
|
|
}
|
|
else if (token == tEND)
|
|
{
|
|
getToken();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
error (eEND);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Generate ENDCASE label and Pop CASE <expression> from stack */
|
|
|
|
pas_GenerateDataOperation(opLABEL, end_case);
|
|
pas_GenerateDataOperation(opINDS, -sINT_SIZE);
|
|
|
|
/* We may have gotten to this point from many different case <statements>.
|
|
* The flag bInvalidateLSP will be set if the LSP is not the same for
|
|
* each of these pathes. Invalidating the LSP will force it to be reloaded
|
|
* when the next level stack access is done.
|
|
*/
|
|
|
|
if (bInvalidateLSP)
|
|
{
|
|
pas_InvalidateCurrentStackLevel();
|
|
}
|
|
}
|
|
|
|
/***********************************************************************/
|
|
static void pas_ForStatement(void)
|
|
{
|
|
STYPE *varPtr;
|
|
uint16_t forLabel = ++label;
|
|
uint16_t endForLabel = ++label;
|
|
uint16_t jmpOp;
|
|
uint16_t modOp;
|
|
int32_t topOfLoopLSP;
|
|
|
|
TRACE(lstFile,"[pas_ForStatement]");
|
|
|
|
/* FOR <assigment statement> <TO, DOWNTO> <expression> DO <statement> */
|
|
|
|
/* Skip over the FOR token */
|
|
|
|
getToken();
|
|
|
|
/* Get and verify the left side of the assignment. */
|
|
if ((token != sINT) && (token != sSUBRANGE))
|
|
error(eINTVAR);
|
|
else
|
|
{
|
|
/* Save the token associated with the left side of the assignment
|
|
* and evaluate the integer assignment.
|
|
*/
|
|
|
|
varPtr = tknPtr;
|
|
getToken();
|
|
|
|
/* Generate the assignment to the integer variable */
|
|
|
|
pas_Assignment(opSTS, exprInteger, tknPtr, tknPtr->sParm.v.parent);
|
|
|
|
/* Determine if this is a TO or a DOWNTO loop and set up the opCodes
|
|
* to generate appropriately.
|
|
*/
|
|
|
|
if (token == tDOWNTO)
|
|
{
|
|
jmpOp = opJGT;
|
|
modOp = opDEC;
|
|
getToken();
|
|
}
|
|
else if (token == tTO)
|
|
{
|
|
jmpOp = opJLT;
|
|
modOp = opINC;
|
|
getToken();
|
|
}
|
|
else
|
|
error (eTOorDOWNTO);
|
|
|
|
/* Evaluate <expression> DO */
|
|
|
|
expression(exprInteger, varPtr->sParm.v.parent);
|
|
|
|
/* Verify that the <expression> is followed by the DO token */
|
|
|
|
if (token != tDO) error (eDO);
|
|
else getToken();
|
|
|
|
/* Generate top of loop label */
|
|
|
|
pas_GenerateDataOperation(opLABEL, forLabel);
|
|
|
|
/* Generate the top of loop comparison. Duplicate the end of loop
|
|
* value, push the current value, and perform the comparison.
|
|
*/
|
|
|
|
pas_GenerateSimple(opDUP);
|
|
pas_GenerateStackReference(opLDS, varPtr);
|
|
pas_GenerateDataOperation(jmpOp, endForLabel);
|
|
|
|
/* Save the level stack pointer (LSP) at the top of the FOR
|
|
* loop. When first executed, this value will depend on
|
|
* logic prior to the loop body. On subsequent loops, this
|
|
* value may be determined by logic within the loop body.
|
|
*/
|
|
|
|
topOfLoopLSP = pas_GetCurrentStackLevel();
|
|
|
|
/* Evaluate the for statement <statement> */
|
|
|
|
statement();
|
|
|
|
/* Generate end of loop logic: Load the variable, modify the
|
|
* variable, store the variable, and jump unconditionally to the
|
|
* top of the loop.
|
|
*/
|
|
|
|
pas_GenerateStackReference(opLDS, varPtr);
|
|
pas_GenerateSimple(modOp);
|
|
pas_GenerateStackReference(opSTS, varPtr);
|
|
pas_GenerateDataOperation(opJMP, forLabel);
|
|
|
|
/* Generate the end of loop label. This is where the conditional
|
|
* branch at the top of the loop will come to.
|
|
*/
|
|
|
|
pas_GenerateDataOperation(opLABEL, endForLabel);
|
|
pas_GenerateDataOperation(opINDS, -sINT_SIZE);
|
|
|
|
/* We always get here from the check at the top of the loop.
|
|
* Normally this will be from the branch from the bottom of
|
|
* the loop to the top of the loop. Then from the conditional
|
|
* branch at the top of the loop to here.
|
|
*
|
|
* But, we need to allow for the special case when the body
|
|
* of the for loop never executed. In this case, the LSP at
|
|
* the first time into the loop may differ from the LSP at
|
|
* subsequent times into the loop. If this is the case, then
|
|
* will will have to invalidate the LSP.
|
|
*/
|
|
|
|
if (topOfLoopLSP != pas_GetCurrentStackLevel())
|
|
{
|
|
/* In thise case, there is uncertainty in the value of the
|
|
* LSP and we must invalidate it. It will be reset to the
|
|
* correct the next time that a level stack reference is
|
|
* performed.
|
|
*/
|
|
|
|
pas_InvalidateCurrentStackLevel();
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************/
|
|
static void pas_WithStatement(void)
|
|
{
|
|
WTYPE saveWithRecord;
|
|
|
|
TRACE(lstFile,"[pas_WithStatement]");
|
|
|
|
/* Generate WITH <variable[,variable[...]] DO <statement> */
|
|
|
|
/* Save the current WITH pointer. Only one WITH can be active at
|
|
* any given time.
|
|
*/
|
|
|
|
saveWithRecord = withRecord;
|
|
|
|
/* Process each RECORD or RECORD OBJECT in the <variable> list */
|
|
|
|
getToken();
|
|
for(;;)
|
|
{
|
|
/* A RECORD type variable may be used in the WITH statement only if
|
|
* there is no other WITH active
|
|
*/
|
|
|
|
if ((token == sRECORD) && (!withRecord.parent))
|
|
{
|
|
/* Save the RECORD variable as the new withRecord */
|
|
|
|
withRecord.level = tknPtr->sLevel;
|
|
withRecord.pointer = false;
|
|
withRecord.varParm = false;
|
|
withRecord.offset = tknPtr->sParm.v.offset;
|
|
withRecord.parent = tknPtr->sParm.v.parent;
|
|
|
|
/* Skip over the RECORD variable */
|
|
|
|
getToken();
|
|
}
|
|
|
|
/* A RECORD VAR parameter may also be used in the WITH statement
|
|
* (again only if there is no other WITH active)
|
|
*/
|
|
|
|
else if ((token == sVAR_PARM) &&
|
|
(!withRecord.parent) &&
|
|
(tknPtr->sParm.v.parent->sParm.t.type == sRECORD))
|
|
{
|
|
/* Save the RECORD VAR parameter as the new withRecord */
|
|
|
|
withRecord.level = tknPtr->sLevel;
|
|
withRecord.pointer = true;
|
|
withRecord.varParm = true;
|
|
withRecord.offset = tknPtr->sParm.v.offset;
|
|
withRecord.parent = tknPtr->sParm.v.parent;
|
|
|
|
/* Skip over the RECORD VAR parameter */
|
|
|
|
getToken();
|
|
}
|
|
|
|
/* A pointer to a RECORD may also be used in the WITH statement
|
|
* (again only if there is no other WITH active)
|
|
*/
|
|
|
|
else if ((token == sPOINTER) &&
|
|
(!withRecord.parent) &&
|
|
(tknPtr->sParm.v.parent->sParm.t.type == sRECORD))
|
|
{
|
|
/* Save the RECORD pointer as the new withRecord */
|
|
|
|
withRecord.level = tknPtr->sLevel;
|
|
withRecord.pointer = true;
|
|
withRecord.pointer = false;
|
|
withRecord.offset = tknPtr->sParm.v.offset;
|
|
withRecord.parent = tknPtr->sParm.v.parent;
|
|
|
|
/* Skip over the RECORD pointer */
|
|
|
|
getToken();
|
|
|
|
/* Verify that deferencing is specified! */
|
|
|
|
if (token != '^') error(eRECORDVAR);
|
|
else getToken();
|
|
}
|
|
|
|
/* A RECORD_OBJECT may be used in the WITH statement if the field
|
|
* is from the same sRECORD type and is itself of type RECORD.
|
|
*/
|
|
|
|
else if ((token == sRECORD_OBJECT) &&
|
|
(tknPtr->sParm.r.record == withRecord.parent) &&
|
|
(tknPtr->sParm.r.parent->sParm.t.type == sRECORD))
|
|
{
|
|
/* Okay, update the withRecord to use this record field */
|
|
|
|
if (withRecord.pointer)
|
|
withRecord.index += tknPtr->sParm.r.offset;
|
|
else
|
|
withRecord.offset += tknPtr->sParm.r.offset;
|
|
|
|
withRecord.parent = tknPtr->sParm.r.parent;
|
|
|
|
/* Skip over the sRECORD_OBJECT */
|
|
|
|
getToken();
|
|
}
|
|
|
|
/* Anything else is an error */
|
|
|
|
else
|
|
{
|
|
error(eRECORDVAR);
|
|
break;
|
|
}
|
|
|
|
|
|
/* Check if there are multiple variables in the WITH statement */
|
|
|
|
if (token == ',') getToken();
|
|
else break;
|
|
}
|
|
|
|
/* Verify that the RECORD list is terminated with DO */
|
|
|
|
if (token != tDO) error (eDO);
|
|
else getToken();
|
|
|
|
/* Then process the statement following the WITH */
|
|
|
|
statement();
|
|
|
|
/* Restore the previous value of the withRecord */
|
|
|
|
withRecord = saveWithRecord;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|