/********************************************************************** * punit.c * Parse a pascal unit file * * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * 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 #include #include #include #include #include #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 */