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
600 lines
17 KiB
C
600 lines
17 KiB
C
/**********************************************************************
|
|
* punit.c
|
|
* Parse a pascal unit file
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "keywords.h"
|
|
#include "pasdefs.h"
|
|
#include "ptdefs.h"
|
|
#include "podefs.h"
|
|
#include "pedefs.h"
|
|
#include "poff.h" /* FHT_ definitions */
|
|
|
|
#include "pas.h" /* for globals */
|
|
#include "pblck.h" /* for block(), constantDefinitionGroup(), etc. */
|
|
#include "pgen.h" /* for pas_Generate*() */
|
|
#include "ptkn.h" /* for getToken() */
|
|
#include "ptbl.h" /* for addFile() */
|
|
#include "pofflib.h" /* For poff*() functions*/
|
|
#include "perr.h" /* for error() */
|
|
#include "pprgm.h" /* for usesSection() */
|
|
#include "punit.h"
|
|
|
|
/***********************************************************************
|
|
* Pre-processor Definitions
|
|
***********************************************************************/
|
|
|
|
#define intAlign(x) (((x) + (sINT_SIZE-1)) & (~(sINT_SIZE-1)))
|
|
|
|
/***********************************************************************
|
|
* Private Function Prototypes
|
|
***********************************************************************/
|
|
|
|
static void interfaceSection (void);
|
|
static void exportedProcedureHeading (void);
|
|
static void exportedFunctionHeading (void);
|
|
|
|
/***********************************************************************
|
|
* Global Functions
|
|
***********************************************************************/
|
|
/* This function is called only main() when the first token parsed out
|
|
* the specified file is 'unit'. In this case, we are parsing a unit file
|
|
* and generating a unit binary.
|
|
*/
|
|
|
|
void unitImplementation(void)
|
|
{
|
|
char *saveTknStart = tkn_strt;
|
|
|
|
TRACE(lstFile, "[unitImplementation]");
|
|
|
|
/* FORM: unit =
|
|
* unit-heading ';' interface-section implementation-section
|
|
* init-section '.'
|
|
* FORM: unit-heading = 'unit' identifer
|
|
* FORM: interface-section =
|
|
* 'interface' [ uses-section ] interface-declaration
|
|
* FORM: implementation-section =
|
|
* 'implementation' [ uses-section ] declaration-group
|
|
* FORM: init-section =
|
|
* 'initialization statement-sequence
|
|
* ['finalization' statement-sequence] 'end' |
|
|
* compound-statement | 'end'
|
|
*
|
|
* On entry, the 'unit' keyword has already been parsed. The
|
|
* current token should point to the identifier following unit.
|
|
*/
|
|
|
|
/* Skip over the unit identifier (the caller has already verified
|
|
* that we are processing the correct unit).
|
|
*/
|
|
|
|
if (token != tIDENT) error(eIDENT);
|
|
|
|
/* Set a UNIT indication in the output poff file header */
|
|
|
|
poffSetFileType(poffHandle, FHT_UNIT, 0, tkn_strt);
|
|
poffSetArchitecture(poffHandle, FHA_PCODE);
|
|
|
|
/* Discard the unit name and get the next token */
|
|
|
|
stringSP = saveTknStart;
|
|
getToken();
|
|
|
|
/* Skip over the semicolon separating the unit-heading from the
|
|
* interface-section.
|
|
*/
|
|
|
|
if (token != ';') error(eSEMICOLON);
|
|
else getToken();
|
|
|
|
/* Verify that the interface-section is present
|
|
* FORM: interface-section =
|
|
* 'interface' [ uses-section ] interface-declaration
|
|
*/
|
|
|
|
interfaceSection();
|
|
|
|
/* Verify that the implementation section is present
|
|
* FORM: implementation-section =
|
|
* 'implementation' [ uses-section ] declaration-group
|
|
*/
|
|
|
|
if (token != tIMPLEMENTATION) error(eIMPLEMENTATION);
|
|
else getToken();
|
|
|
|
FP->section = eIsImplementationSection;
|
|
|
|
/* Check for the presence of an optional uses-section */
|
|
|
|
if (token == tUSES)
|
|
{
|
|
/* Process the uses-section */
|
|
|
|
getToken();
|
|
usesSection();
|
|
}
|
|
|
|
/* Now, process the declaration-group
|
|
*
|
|
* FORM: implementation-section =
|
|
* 'implementation' [ uses-section ] declaration-group
|
|
* FORM: init-section =
|
|
* 'initialization statement-sequence
|
|
* ['finalization' statement-sequence] 'end' |
|
|
* compound-statement | 'end'
|
|
*/
|
|
|
|
declarationGroup(0);
|
|
|
|
/* Process the init-section
|
|
* FORM: init-section =
|
|
* 'initialization statement-sequence
|
|
* ['finalization' statement-sequence] 'end' |
|
|
* compound-statement | 'end'
|
|
*
|
|
* Not yet... for now, we only require the 'end'
|
|
*/
|
|
|
|
FP->section = eIsInitializationSection;
|
|
if (token != tEND) error(eEND);
|
|
else getToken();
|
|
|
|
FP->section = eIsOtherSection;
|
|
|
|
/* Verify that the unit file ends with a period */
|
|
|
|
if (token != '.') error(ePERIOD);
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* This logic is called from usersSection after any a uses-section is
|
|
* encountered in any file at any level. In this case, we are only
|
|
* going to parse the interface section from the unit file.
|
|
*/
|
|
|
|
void unitInterface(void)
|
|
{
|
|
int32_t savedDStack = dstack;
|
|
TRACE(lstFile, "[unitInterface]");
|
|
|
|
/* FORM: unit =
|
|
* unit-heading ';' interface-section implementation-section
|
|
* init-section
|
|
* FORM: unit-heading = 'unit' identifer
|
|
*
|
|
* On entry, the 'unit' keyword has already been parsed. The
|
|
* current token should point to the identifier following unit.
|
|
*/
|
|
|
|
/* Skip over the unit identifier (the caller has already verified
|
|
* that we are processing the correct unit).
|
|
*/
|
|
|
|
if (token != tIDENT) error(eIDENT);
|
|
else getToken();
|
|
|
|
/* Skip over the semicolon separating the unit-heading from the
|
|
* interface-section.
|
|
*/
|
|
|
|
if (token != ';') error(eSEMICOLON);
|
|
else getToken();
|
|
|
|
/* Process the interface-section
|
|
* FORM: interface-section =
|
|
* 'interface' [ uses-section ] interface-declaration
|
|
*/
|
|
|
|
interfaceSection();
|
|
|
|
/* Verify that the implementation section is present
|
|
* FORM: implementation-section =
|
|
* 'implementation' [ uses-section ] declaration-group
|
|
*/
|
|
|
|
if (token != tIMPLEMENTATION) error(eIMPLEMENTATION);
|
|
|
|
/* Then just ignore the rest of the file. We'll let the compilation
|
|
* of the unit file check the correctness of the implementation.
|
|
*/
|
|
|
|
FP->section = eIsOtherSection;
|
|
|
|
/* If we are generating a program binary, then all variables declared
|
|
* by this logic a bonafide. But if are generating UNIT binary, then
|
|
* all variables declared as imported with a relative stack offset.
|
|
* In this case, we must release any data stack allocated in this
|
|
* process.
|
|
*/
|
|
|
|
dstack = savedDStack;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Private Functions
|
|
***********************************************************************/
|
|
|
|
static void interfaceSection(void)
|
|
{
|
|
int16_t saveNSym = nsym; /* Save top of symbol table */
|
|
int16_t saveNConst = nconst; /* Save top of constant table */
|
|
|
|
TRACE(lstFile, "[interfaceSection]");
|
|
|
|
/* FORM: interface-section =
|
|
* 'interface' [ uses-section ] interface-declaration
|
|
*
|
|
* On entry, the unit-heading keyword has already been parsed. The
|
|
* current token should point to the identifier following unit.
|
|
*/
|
|
|
|
if (token != tINTERFACE) error(eINTERFACE);
|
|
else getToken();
|
|
|
|
FP->section = eIsInterfaceSection;
|
|
|
|
/* Check for the presence of an optional uses-section */
|
|
|
|
if (token == tUSES)
|
|
{
|
|
/* Process the uses-section */
|
|
|
|
getToken();
|
|
usesSection();
|
|
}
|
|
|
|
/* Process the interface-declaration
|
|
*
|
|
* FORM: interface-declaration =
|
|
* [ constant-definition-group ] [ type-definition-group ]
|
|
* [ variable-declaration-group ] exported-heading
|
|
*/
|
|
|
|
/* Process optional constant-definition-group.
|
|
* FORM: constant-definition-group =
|
|
* 'const' constant-definition ';' { constant-definition ';' }
|
|
*/
|
|
|
|
if (token == tCONST)
|
|
{
|
|
const_strt = saveNConst; /* Limit search to present level */
|
|
getToken(); /* Get identifier */
|
|
const_strt = 0;
|
|
|
|
/* Process constant-definition.
|
|
* FORM: constant-definition = identifier '=' constant
|
|
*/
|
|
|
|
constantDefinitionGroup();
|
|
|
|
} /* end if */
|
|
|
|
/* Process type-definition-group
|
|
* FORM: type-definition-group =
|
|
* 'type' type-definition ';' { type-definition ';' }
|
|
*/
|
|
|
|
if (token == tTYPE)
|
|
{
|
|
const_strt = saveNConst; /* Limit search to present level */
|
|
sym_strt = saveNSym;
|
|
getToken(); /* Get identifier */
|
|
const_strt = 0;
|
|
sym_strt = 0;
|
|
|
|
/* Process the type-definitions in the type-definition-group
|
|
* FORM: type-definition = identifier '=' type-denoter
|
|
*/
|
|
|
|
typeDefinitionGroup();
|
|
} /* end if */
|
|
|
|
/* Process the optional variable-declaration-group
|
|
* FORM: variable-declaration-group =
|
|
* 'var' variable-declaration { ';' variable-declaration }
|
|
*/
|
|
|
|
if (token == tVAR)
|
|
{
|
|
const_strt = saveNConst; /* Limit search to present level */
|
|
sym_strt = saveNSym;
|
|
getToken(); /* Get identifier */
|
|
const_strt = 0;
|
|
sym_strt = 0;
|
|
|
|
/* Process the variable declarations
|
|
* FORM: variable-declaration = identifier-list ':' type-denoter
|
|
* FORM: identifier-list = identifier { ',' identifier }
|
|
*/
|
|
|
|
variableDeclarationGroup();
|
|
} /* end if */
|
|
|
|
/* Process the exported-heading
|
|
*
|
|
* FORM: exported-heading =
|
|
* procedure-heading ';' [ directive ] |
|
|
* function-heading ';' [ directive ]
|
|
*/
|
|
|
|
for (;;)
|
|
{
|
|
/* FORM: function-heading =
|
|
* 'function' function-identifier [ formal-parameter-list ]
|
|
* ':' result-type
|
|
*/
|
|
|
|
if (token == tFUNCTION)
|
|
{
|
|
const_strt = saveNConst; /* Limit search to present level */
|
|
sym_strt = saveNSym;
|
|
getToken(); /* Get identifier */
|
|
const_strt = 0;
|
|
sym_strt = 0;
|
|
|
|
/* Process the interface declaration */
|
|
|
|
exportedFunctionHeading();
|
|
} /* end if */
|
|
|
|
/* FORM: procedure-heading =
|
|
* 'procedure' procedure-identifier [ formal-parameter-list ]
|
|
*/
|
|
|
|
else if (token == tPROCEDURE)
|
|
{
|
|
const_strt = saveNConst; /* Limit search to present level */
|
|
sym_strt = saveNSym;
|
|
getToken(); /* Get identifier */
|
|
const_strt = 0;
|
|
sym_strt = 0;
|
|
|
|
/* Process the interface declaration */
|
|
|
|
exportedProcedureHeading();
|
|
} /* end else if */
|
|
else break;
|
|
} /* end for */
|
|
|
|
/* We are finished with the interface section */
|
|
|
|
FP->section = eIsOtherSection;
|
|
}
|
|
|
|
/* Process Procedure Declaration Block */
|
|
|
|
static void exportedProcedureHeading(void)
|
|
{
|
|
uint16_t procLabel = ++label;
|
|
char *saveChSp;
|
|
STYPE *procPtr;
|
|
register int i;
|
|
|
|
TRACE(lstFile,"[exportedProcedureHeading]");
|
|
|
|
/* FORM: procedure-heading =
|
|
* 'procedure' identifier [ formal-parameter-list ]
|
|
* FORM: procedure-identifier = identifier
|
|
*
|
|
* On entry, token refers to token AFTER the 'procedure' reserved
|
|
* word.
|
|
*/
|
|
|
|
/* Process the procedure-heading */
|
|
|
|
if (token != tIDENT)
|
|
{
|
|
error (eIDENT);
|
|
return;
|
|
} /* endif */
|
|
|
|
procPtr = addProcedure(tkn_strt, sPROC, procLabel, 0, NULL);
|
|
|
|
/* Mark the procedure as external */
|
|
|
|
procPtr->sParm.p.flags |= SPROC_EXTERNAL;
|
|
|
|
/* Save the string stack pointer so that we can release all
|
|
* formal parameter strings later. Then get the next token.
|
|
*/
|
|
|
|
saveChSp = stringSP;
|
|
getToken();
|
|
|
|
/* NOTE: The level associated with the PROCEDURE symbol is the level
|
|
* At which the procedure was declared. Everything declare within the
|
|
* PROCEDURE is at the next level
|
|
*/
|
|
|
|
level++;
|
|
|
|
/* Process parameter list */
|
|
|
|
(void)formalParameterList(procPtr);
|
|
|
|
if (token != ';') error (eSEMICOLON);
|
|
else getToken();
|
|
|
|
/* If we are compiling a program or unit that "imports" the
|
|
* procedure then generate the appropriate symbol table entries
|
|
* in the output file to support relocation when the external
|
|
* procedure is called.
|
|
*/
|
|
|
|
if (includeIndex > 0)
|
|
{
|
|
pas_GenerateProcImport(procPtr);
|
|
}
|
|
|
|
/* Destroy formal parameter names */
|
|
|
|
for (i = 1; i <= procPtr->sParm.p.nParms; i++)
|
|
{
|
|
procPtr[i].sName = NULL;
|
|
}
|
|
stringSP = saveChSp;
|
|
|
|
/* Drop the level back to where it was */
|
|
|
|
level--;
|
|
|
|
} /* end exportedProcedureHeading */
|
|
|
|
/***************************************************************/
|
|
/* Process Function Declaration Block */
|
|
|
|
static void exportedFunctionHeading(void)
|
|
{
|
|
uint16_t funcLabel = ++label;
|
|
int16_t parameterOffset;
|
|
char *saveChSp;
|
|
STYPE *funcPtr;
|
|
register int i;
|
|
|
|
TRACE(lstFile,"[exportedFunctionHeading]");
|
|
|
|
/* FORM: function-declaration =
|
|
* function-heading ';' directive |
|
|
* function-heading ';' function-block
|
|
* FORM: function-heading =
|
|
* 'function' function-identifier [ formal-parameter-list ]
|
|
* ':' result-type
|
|
*
|
|
* On entry token should lrefer to the function-identifier.
|
|
*/
|
|
|
|
/* Verify function-identifier */
|
|
|
|
if (token != tIDENT)
|
|
{
|
|
error (eIDENT);
|
|
return;
|
|
} /* endif */
|
|
|
|
funcPtr = addProcedure(tkn_strt, sFUNC, funcLabel, 0, NULL);
|
|
|
|
/* Mark the procedure as external */
|
|
|
|
funcPtr->sParm.p.flags |= SPROC_EXTERNAL;
|
|
|
|
/* NOTE: The level associated with the FUNCTION symbol is the level
|
|
* At which the procedure was declared. Everything declare within the
|
|
* PROCEDURE is at the next level
|
|
*/
|
|
|
|
level++;
|
|
|
|
/* Save the string stack pointer so that we can release all
|
|
* formal parameter strings later. Then get the next token.
|
|
*/
|
|
|
|
saveChSp = stringSP;
|
|
getToken();
|
|
|
|
/* Process parameter list */
|
|
|
|
parameterOffset = formalParameterList(funcPtr);
|
|
|
|
/* Verify that the parameter list is followed by a colon */
|
|
|
|
if (token != ':') error (eCOLON);
|
|
else getToken();
|
|
|
|
/* Get function type, return value type/size and offset to return value */
|
|
|
|
if (token == sTYPE)
|
|
{
|
|
/* The offset to the return value is the offset to the last
|
|
* parameter minus the size of the return value (aligned to
|
|
* multiples of size of INTEGER).
|
|
*/
|
|
|
|
parameterOffset -= tknPtr->sParm.t.rsize;
|
|
parameterOffset = intAlign(parameterOffset);
|
|
|
|
/* Save the TYPE for the function */
|
|
|
|
funcPtr->sParm.p.parent = tknPtr;
|
|
|
|
/* Skip over the result-type token */
|
|
|
|
getToken();
|
|
} /* end if */
|
|
else
|
|
{
|
|
error(eINVTYPE);
|
|
}
|
|
|
|
/* Verify the final semicolon */
|
|
|
|
if (token != ';') error (eSEMICOLON);
|
|
else getToken();
|
|
|
|
/* If we are compiling a program or unit that "imports" the
|
|
* function then generate the appropriate symbol table entries
|
|
* in the output file to support relocation when the external
|
|
* function is called.
|
|
*/
|
|
|
|
if (includeIndex > 0)
|
|
{
|
|
pas_GenerateProcImport(funcPtr);
|
|
}
|
|
|
|
/* Destroy formal parameter names and the function return value name */
|
|
|
|
for (i = 1; i <= funcPtr->sParm.p.nParms; i++)
|
|
{
|
|
funcPtr[i].sName = ((char *) NULL);
|
|
}
|
|
stringSP = saveChSp;
|
|
|
|
/* Restore the original level */
|
|
|
|
level--;
|
|
|
|
} /* end exportedFunctionHeading */
|