hmsl: support EOT to force BYE
[pforth] / csrc / pfcompil.c
/* @(#) pfcompil.c 98/01/26 1.5 */
/***************************************************************
** Compiler for PForth based on 'C'
**
** These routines could be left out of an execute only version.
**
** Author: Phil Burk
** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom
**
** The pForth software code is dedicated to the public domain,
** and any third party may reproduce, distribute and modify
** the pForth software code or any derivative works thereof
** without any compensation or license. The pForth software
** code is provided on an "as is" basis without any warranty
** of any kind, including, without limitation, the implied
** warranties of merchantability and fitness for a particular
** purpose and their equivalents under the laws of any jurisdiction.
**
****************************************************************
** 941004 PLB Extracted IO calls from pforth_main.c
** 950320 RDG Added underflow checking for FP stack
***************************************************************/
#include "pf_all.h"
#include "pfcompil.h"
#define ABORT_RETURN_CODE (10)
#define UINT32_MASK ((sizeof(ucell_t)-1))
/***************************************************************/
/************** Static Prototypes ******************************/
/***************************************************************/
static void ffStringColon( const ForthStringPtr FName );
static cell_t CheckRedefinition( const ForthStringPtr FName );
static void ffUnSmudge( void );
static cell_t FindAndCompile( const char *theWord );
static cell_t ffCheckDicRoom( void );
#ifndef PF_NO_INIT
static void CreateDeferredC( ExecToken DefaultXT, const char *CName );
#endif
cell_t NotCompiled( const char *FunctionName )
{
MSG("Function ");
MSG(FunctionName);
MSG(" not compiled in this version of PForth.\n");
return -1;
}
#ifndef PF_NO_SHELL
/***************************************************************
** Create an entry in the Dictionary for the given ExecutionToken.
** FName is name in Forth format.
*/
void CreateDicEntry( ExecToken XT, const ForthStringPtr FName, ucell_t Flags )
{
cfNameLinks *cfnl;
cfnl = (cfNameLinks *) gCurrentDictionary->dic_HeaderPtr;
/* Set link to previous header, if any. */
if( gVarContext )
{
WRITE_CELL_DIC( &cfnl->cfnl_PreviousName, ABS_TO_NAMEREL( gVarContext ) );
}
else
{
cfnl->cfnl_PreviousName = 0;
}
/* Put Execution token in header. */
WRITE_CELL_DIC( &cfnl->cfnl_ExecToken, XT );
/* Advance Header Dictionary Pointer */
gCurrentDictionary->dic_HeaderPtr += sizeof(cfNameLinks);
/* Laydown name. */
gVarContext = gCurrentDictionary->dic_HeaderPtr;
pfCopyMemory( (uint8_t *) gCurrentDictionary->dic_HeaderPtr, FName, (*FName)+1 );
gCurrentDictionary->dic_HeaderPtr += (*FName)+1;
/* Set flags. */
*(char*)gVarContext |= (char) Flags;
/* Align to quad byte boundaries with zeroes. */
while( gCurrentDictionary->dic_HeaderPtr & UINT32_MASK )
{
*(char*)(gCurrentDictionary->dic_HeaderPtr++) = 0;
}
}
/***************************************************************
** Convert name then create dictionary entry.
*/
void CreateDicEntryC( ExecToken XT, const char *CName, ucell_t Flags )
{
ForthString FName[40];
CStringToForth( FName, CName, sizeof(FName) );
CreateDicEntry( XT, FName, Flags );
}
/***************************************************************
** Convert absolute namefield address to previous absolute name
** field address or NULL.
*/
const ForthString *NameToPrevious( const ForthString *NFA )
{
cell_t RelNamePtr;
const cfNameLinks *cfnl;
/* DBUG(("\nNameToPrevious: NFA = 0x%x\n", (cell_t) NFA)); */
cfnl = (const cfNameLinks *) ( ((const char *) NFA) - sizeof(cfNameLinks) );
RelNamePtr = READ_CELL_DIC((const cell_t *) (&cfnl->cfnl_PreviousName));
/* DBUG(("\nNameToPrevious: RelNamePtr = 0x%x\n", (cell_t) RelNamePtr )); */
if( RelNamePtr )
{
return ( (ForthString *) NAMEREL_TO_ABS( RelNamePtr ) );
}
else
{
return NULL;
}
}
/***************************************************************
** Convert NFA to ExecToken.
*/
ExecToken NameToToken( const ForthString *NFA )
{
const cfNameLinks *cfnl;
/* Convert absolute namefield address to absolute link field address. */
cfnl = (const cfNameLinks *) ( ((const char *) NFA) - sizeof(cfNameLinks) );
return READ_CELL_DIC((const cell_t *) (&cfnl->cfnl_ExecToken));
}
/***************************************************************
** Find XTs needed by compiler.
*/
cell_t FindSpecialXTs( void )
{
if( ffFindC( "(QUIT)", &gQuitP_XT ) == 0) goto nofind;
if( ffFindC( "NUMBER?", &gNumberQ_XT ) == 0) goto nofind;
if( ffFindC( "ACCEPT", &gAcceptP_XT ) == 0) goto nofind;
DBUG(("gNumberQ_XT = 0x%x\n", (unsigned int)gNumberQ_XT ));
return 0;
nofind:
ERR("FindSpecialXTs failed!\n");
return -1;
}
/***************************************************************
** Build a dictionary from scratch.
*/
#ifndef PF_NO_INIT
PForthDictionary pfBuildDictionary( cell_t HeaderSize, cell_t CodeSize )
{
pfDictionary_t *dic;
dic = pfCreateDictionary( HeaderSize, CodeSize );
if( !dic ) goto nomem;
pfDebugMessage("pfBuildDictionary: Start adding dictionary entries.\n");
gCurrentDictionary = dic;
gNumPrimitives = NUM_PRIMITIVES;
CreateDicEntryC( ID_EXIT, "EXIT", 0 );
pfDebugMessage("pfBuildDictionary: added EXIT\n");
CreateDicEntryC( ID_1MINUS, "1-", 0 );
pfDebugMessage("pfBuildDictionary: added 1-\n");
CreateDicEntryC( ID_1PLUS, "1+", 0 );
CreateDicEntryC( ID_2_R_FETCH, "2R@", 0 );
CreateDicEntryC( ID_2_R_FROM, "2R>", 0 );
CreateDicEntryC( ID_2_TO_R, "2>R", 0 );
CreateDicEntryC( ID_2DUP, "2DUP", 0 );
CreateDicEntryC( ID_2LITERAL, "2LITERAL", FLAG_IMMEDIATE );
CreateDicEntryC( ID_2LITERAL_P, "(2LITERAL)", 0 );
CreateDicEntryC( ID_2MINUS, "2-", 0 );
CreateDicEntryC( ID_2PLUS, "2+", 0 );
CreateDicEntryC( ID_2OVER, "2OVER", 0 );
CreateDicEntryC( ID_2SWAP, "2SWAP", 0 );
CreateDicEntryC( ID_ACCEPT_P, "(ACCEPT)", 0 );
CreateDeferredC( ID_ACCEPT_P, "ACCEPT" );
CreateDicEntryC( ID_ALITERAL, "ALITERAL", FLAG_IMMEDIATE );
CreateDicEntryC( ID_ALITERAL_P, "(ALITERAL)", 0 );
CreateDicEntryC( ID_ALLOCATE, "ALLOCATE", 0 );
pfDebugMessage("pfBuildDictionary: added ALLOCATE\n");
CreateDicEntryC( ID_ARSHIFT, "ARSHIFT", 0 );
CreateDicEntryC( ID_AND, "AND", 0 );
CreateDicEntryC( ID_BAIL, "BAIL", 0 );
CreateDicEntryC( ID_BRANCH, "BRANCH", 0 );
CreateDicEntryC( ID_BODY_OFFSET, "BODY_OFFSET", 0 );
CreateDicEntryC( ID_BYE, "BYE", 0 );
CreateDicEntryC( ID_CATCH, "CATCH", 0 );
CreateDicEntryC( ID_CELL, "CELL", 0 );
CreateDicEntryC( ID_CELLS, "CELLS", 0 );
CreateDicEntryC( ID_CFETCH, "C@", 0 );
CreateDicEntryC( ID_CMOVE, "CMOVE", 0 );
CreateDicEntryC( ID_CMOVE_UP, "CMOVE>", 0 );
CreateDicEntryC( ID_COLON, ":", 0 );
CreateDicEntryC( ID_COLON_P, "(:)", 0 );
CreateDicEntryC( ID_COMPARE, "COMPARE", 0 );
CreateDicEntryC( ID_COMP_EQUAL, "=", 0 );
CreateDicEntryC( ID_COMP_NOT_EQUAL, "<>", 0 );
CreateDicEntryC( ID_COMP_GREATERTHAN, ">", 0 );
CreateDicEntryC( ID_COMP_U_GREATERTHAN, "U>", 0 );
pfDebugMessage("pfBuildDictionary: added U>\n");
CreateDicEntryC( ID_COMP_LESSTHAN, "<", 0 );
CreateDicEntryC( ID_COMP_U_LESSTHAN, "U<", 0 );
CreateDicEntryC( ID_COMP_ZERO_EQUAL, "0=", 0 );
CreateDicEntryC( ID_COMP_ZERO_NOT_EQUAL, "0<>", 0 );
CreateDicEntryC( ID_COMP_ZERO_GREATERTHAN, "0>", 0 );
CreateDicEntryC( ID_COMP_ZERO_LESSTHAN, "0<", 0 );
CreateDicEntryC( ID_CR, "CR", 0 );
CreateDicEntryC( ID_CREATE, "CREATE", 0 );
CreateDicEntryC( ID_CREATE_P, "(CREATE)", 0 );
CreateDicEntryC( ID_D_PLUS, "D+", 0 );
CreateDicEntryC( ID_D_MINUS, "D-", 0 );
CreateDicEntryC( ID_D_UMSMOD, "UM/MOD", 0 );
CreateDicEntryC( ID_D_MUSMOD, "MU/MOD", 0 );
CreateDicEntryC( ID_D_MTIMES, "M*", 0 );
pfDebugMessage("pfBuildDictionary: added M*\n");
CreateDicEntryC( ID_D_UMTIMES, "UM*", 0 );
CreateDicEntryC( ID_DEFER, "DEFER", 0 );
CreateDicEntryC( ID_CSTORE, "C!", 0 );
CreateDicEntryC( ID_DEPTH, "DEPTH", 0 );
pfDebugMessage("pfBuildDictionary: added DEPTH\n");
CreateDicEntryC( ID_DIVIDE, "/", 0 );
CreateDicEntryC( ID_DOT, ".", 0 );
CreateDicEntryC( ID_DOTS, ".S", 0 );
pfDebugMessage("pfBuildDictionary: added .S\n");
CreateDicEntryC( ID_DO_P, "(DO)", 0 );
CreateDicEntryC( ID_DROP, "DROP", 0 );
CreateDicEntryC( ID_DUMP, "DUMP", 0 );
CreateDicEntryC( ID_DUP, "DUP", 0 );
CreateDicEntryC( ID_EMIT_P, "(EMIT)", 0 );
pfDebugMessage("pfBuildDictionary: added (EMIT)\n");
CreateDeferredC( ID_EMIT_P, "EMIT");
pfDebugMessage("pfBuildDictionary: added EMIT\n");
CreateDicEntryC( ID_EOL, "EOL", 0 );
CreateDicEntryC( ID_ERRORQ_P, "(?ERROR)", 0 );
CreateDicEntryC( ID_ERRORQ_P, "?ERROR", 0 );
CreateDicEntryC( ID_EXECUTE, "EXECUTE", 0 );
CreateDicEntryC( ID_FETCH, "@", 0 );
CreateDicEntryC( ID_FILL, "FILL", 0 );
CreateDicEntryC( ID_FIND, "FIND", 0 );
CreateDicEntryC( ID_FILE_CREATE, "CREATE-FILE", 0 );
CreateDicEntryC( ID_FILE_DELETE, "DELETE-FILE", 0 );
CreateDicEntryC( ID_FILE_OPEN, "OPEN-FILE", 0 );
CreateDicEntryC( ID_FILE_CLOSE, "CLOSE-FILE", 0 );
CreateDicEntryC( ID_FILE_READ, "READ-FILE", 0 );
CreateDicEntryC( ID_FILE_SIZE, "FILE-SIZE", 0 );
CreateDicEntryC( ID_FILE_WRITE, "WRITE-FILE", 0 );
CreateDicEntryC( ID_FILE_POSITION, "FILE-POSITION", 0 );
CreateDicEntryC( ID_FILE_REPOSITION, "REPOSITION-FILE", 0 );
CreateDicEntryC( ID_FILE_FLUSH, "FLUSH-FILE", 0 );
CreateDicEntryC( ID_FILE_RENAME, "(RENAME-FILE)", 0 );
CreateDicEntryC( ID_FILE_RESIZE, "(RESIZE-FILE)", 0 );
CreateDicEntryC( ID_FILE_RO, "R/O", 0 );
CreateDicEntryC( ID_FILE_RW, "R/W", 0 );
CreateDicEntryC( ID_FILE_WO, "W/O", 0 );
CreateDicEntryC( ID_FILE_BIN, "BIN", 0 );
CreateDicEntryC( ID_FINDNFA, "FINDNFA", 0 );
CreateDicEntryC( ID_FLUSHEMIT, "FLUSHEMIT", 0 );
CreateDicEntryC( ID_FREE, "FREE", 0 );
#include "pfcompfp.h"
CreateDicEntryC( ID_HERE, "HERE", 0 );
CreateDicEntryC( ID_NUMBERQ_P, "(SNUMBER?)", 0 );
CreateDicEntryC( ID_I, "I", 0 );
CreateDicEntryC( ID_INTERPRET, "INTERPRET", 0 );
CreateDicEntryC( ID_J, "J", 0 );
CreateDicEntryC( ID_INCLUDE_FILE, "INCLUDE-FILE", 0 );
CreateDicEntryC( ID_KEY, "KEY", 0 );
CreateDicEntryC( ID_LEAVE_P, "(LEAVE)", 0 );
CreateDicEntryC( ID_LITERAL, "LITERAL", FLAG_IMMEDIATE );
CreateDicEntryC( ID_LITERAL_P, "(LITERAL)", 0 );
CreateDicEntryC( ID_LOADSYS, "LOADSYS", 0 );
CreateDicEntryC( ID_LOCAL_COMPILER, "LOCAL-COMPILER", 0 );
CreateDicEntryC( ID_LOCAL_ENTRY, "(LOCAL.ENTRY)", 0 );
CreateDicEntryC( ID_LOCAL_EXIT, "(LOCAL.EXIT)", 0 );
CreateDicEntryC( ID_LOCAL_FETCH, "(LOCAL@)", 0 );
CreateDicEntryC( ID_LOCAL_FETCH_1, "(1_LOCAL@)", 0 );
CreateDicEntryC( ID_LOCAL_FETCH_2, "(2_LOCAL@)", 0 );
CreateDicEntryC( ID_LOCAL_FETCH_3, "(3_LOCAL@)", 0 );
CreateDicEntryC( ID_LOCAL_FETCH_4, "(4_LOCAL@)", 0 );
CreateDicEntryC( ID_LOCAL_FETCH_5, "(5_LOCAL@)", 0 );
CreateDicEntryC( ID_LOCAL_FETCH_6, "(6_LOCAL@)", 0 );
CreateDicEntryC( ID_LOCAL_FETCH_7, "(7_LOCAL@)", 0 );
CreateDicEntryC( ID_LOCAL_FETCH_8, "(8_LOCAL@)", 0 );
CreateDicEntryC( ID_LOCAL_STORE, "(LOCAL!)", 0 );
CreateDicEntryC( ID_LOCAL_STORE_1, "(1_LOCAL!)", 0 );
CreateDicEntryC( ID_LOCAL_STORE_2, "(2_LOCAL!)", 0 );
CreateDicEntryC( ID_LOCAL_STORE_3, "(3_LOCAL!)", 0 );
CreateDicEntryC( ID_LOCAL_STORE_4, "(4_LOCAL!)", 0 );
CreateDicEntryC( ID_LOCAL_STORE_5, "(5_LOCAL!)", 0 );
CreateDicEntryC( ID_LOCAL_STORE_6, "(6_LOCAL!)", 0 );
CreateDicEntryC( ID_LOCAL_STORE_7, "(7_LOCAL!)", 0 );
CreateDicEntryC( ID_LOCAL_STORE_8, "(8_LOCAL!)", 0 );
CreateDicEntryC( ID_LOCAL_PLUSSTORE, "(LOCAL+!)", 0 );
CreateDicEntryC( ID_LOOP_P, "(LOOP)", 0 );
CreateDicEntryC( ID_LSHIFT, "LSHIFT", 0 );
CreateDicEntryC( ID_MAX, "MAX", 0 );
CreateDicEntryC( ID_MIN, "MIN", 0 );
CreateDicEntryC( ID_MINUS, "-", 0 );
CreateDicEntryC( ID_NAME_TO_TOKEN, "NAME>", 0 );
CreateDicEntryC( ID_NAME_TO_PREVIOUS, "PREVNAME", 0 );
CreateDicEntryC( ID_NOOP, "NOOP", 0 );
CreateDeferredC( ID_NUMBERQ_P, "NUMBER?" );
CreateDicEntryC( ID_OR, "OR", 0 );
CreateDicEntryC( ID_OVER, "OVER", 0 );
pfDebugMessage("pfBuildDictionary: added OVER\n");
CreateDicEntryC( ID_PICK, "PICK", 0 );
CreateDicEntryC( ID_PLUS, "+", 0 );
CreateDicEntryC( ID_PLUSLOOP_P, "(+LOOP)", 0 );
CreateDicEntryC( ID_PLUS_STORE, "+!", 0 );
CreateDicEntryC( ID_QUIT_P, "(QUIT)", 0 );
CreateDeferredC( ID_QUIT_P, "QUIT" );
CreateDicEntryC( ID_QDO_P, "(?DO)", 0 );
CreateDicEntryC( ID_QDUP, "?DUP", 0 );
CreateDicEntryC( ID_QTERMINAL, "?TERMINAL", 0 );
CreateDicEntryC( ID_QTERMINAL, "KEY?", 0 );
CreateDicEntryC( ID_REFILL, "REFILL", 0 );
CreateDicEntryC( ID_RESIZE, "RESIZE", 0 );
CreateDicEntryC( ID_ROLL, "ROLL", 0 );
CreateDicEntryC( ID_ROT, "ROT", 0 );
CreateDicEntryC( ID_RSHIFT, "RSHIFT", 0 );
CreateDicEntryC( ID_R_DROP, "RDROP", 0 );
CreateDicEntryC( ID_R_FETCH, "R@", 0 );
CreateDicEntryC( ID_R_FROM, "R>", 0 );
CreateDicEntryC( ID_RP_FETCH, "RP@", 0 );
CreateDicEntryC( ID_RP_STORE, "RP!", 0 );
CreateDicEntryC( ID_SEMICOLON, ";", FLAG_IMMEDIATE );
CreateDicEntryC( ID_SP_FETCH, "SP@", 0 );
CreateDicEntryC( ID_SP_STORE, "SP!", 0 );
CreateDicEntryC( ID_STORE, "!", 0 );
CreateDicEntryC( ID_SAVE_FORTH_P, "(SAVE-FORTH)", 0 );
CreateDicEntryC( ID_SCAN, "SCAN", 0 );
CreateDicEntryC( ID_SKIP, "SKIP", 0 );
CreateDicEntryC( ID_SOURCE, "SOURCE", 0 );
CreateDicEntryC( ID_SOURCE_SET, "SET-SOURCE", 0 );
CreateDicEntryC( ID_SOURCE_ID, "SOURCE-ID", 0 );
CreateDicEntryC( ID_SOURCE_ID_PUSH, "PUSH-SOURCE-ID", 0 );
CreateDicEntryC( ID_SOURCE_ID_POP, "POP-SOURCE-ID", 0 );
CreateDicEntryC( ID_SOURCE_LINE_NUMBER_FETCH, "SOURCE-LINE-NUMBER@", 0 );
CreateDicEntryC( ID_SOURCE_LINE_NUMBER_STORE, "SOURCE-LINE-NUMBER!", 0 );
CreateDicEntryC( ID_SWAP, "SWAP", 0 );
CreateDicEntryC( ID_TEST1, "TEST1", 0 );
CreateDicEntryC( ID_TEST2, "TEST2", 0 );
CreateDicEntryC( ID_TICK, "'", 0 );
CreateDicEntryC( ID_TIMES, "*", 0 );
CreateDicEntryC( ID_THROW, "THROW", 0 );
CreateDicEntryC( ID_TO_R, ">R", 0 );
CreateDicEntryC( ID_TYPE, "TYPE", 0 );
CreateDicEntryC( ID_VAR_BASE, "BASE", 0 );
CreateDicEntryC( ID_VAR_CODE_BASE, "CODE-BASE", 0 );
CreateDicEntryC( ID_VAR_CODE_LIMIT, "CODE-LIMIT", 0 );
CreateDicEntryC( ID_VAR_CONTEXT, "CONTEXT", 0 );
CreateDicEntryC( ID_VAR_DP, "DP", 0 );
CreateDicEntryC( ID_VAR_ECHO, "ECHO", 0 );
CreateDicEntryC( ID_VAR_HEADERS_PTR, "HEADERS-PTR", 0 );
CreateDicEntryC( ID_VAR_HEADERS_BASE, "HEADERS-BASE", 0 );
CreateDicEntryC( ID_VAR_HEADERS_LIMIT, "HEADERS-LIMIT", 0 );
CreateDicEntryC( ID_VAR_NUM_TIB, "#TIB", 0 );
CreateDicEntryC( ID_VAR_RETURN_CODE, "RETURN-CODE", 0 );
CreateDicEntryC( ID_VAR_TRACE_FLAGS, "TRACE-FLAGS", 0 );
CreateDicEntryC( ID_VAR_TRACE_LEVEL, "TRACE-LEVEL", 0 );
CreateDicEntryC( ID_VAR_TRACE_STACK, "TRACE-STACK", 0 );
CreateDicEntryC( ID_VAR_OUT, "OUT", 0 );
CreateDicEntryC( ID_VAR_STATE, "STATE", 0 );
CreateDicEntryC( ID_VAR_TO_IN, ">IN", 0 );
CreateDicEntryC( ID_WORD, "WORD", 0 );
CreateDicEntryC( ID_WORD_FETCH, "W@", 0 );
CreateDicEntryC( ID_WORD_STORE, "W!", 0 );
CreateDicEntryC( ID_XOR, "XOR", 0 );
CreateDicEntryC( ID_ZERO_BRANCH, "0BRANCH", 0 );
pfDebugMessage("pfBuildDictionary: FindSpecialXTs\n");
if( FindSpecialXTs() < 0 ) goto error;
if( CompileCustomFunctions() < 0 ) goto error; /* Call custom 'C' call builder. */
#ifdef PF_DEBUG
DumpMemory( dic->dic_HeaderBase, 256 );
DumpMemory( dic->dic_CodeBase, 256 );
#endif
pfDebugMessage("pfBuildDictionary: Finished adding dictionary entries.\n");
return (PForthDictionary) dic;
error:
pfDebugMessage("pfBuildDictionary: Error adding dictionary entries.\n");
pfDeleteDictionary( dic );
return NULL;
nomem:
return NULL;
}
#endif /* !PF_NO_INIT */
/*
** ( xt -- nfa 1 , x 0 , find NFA in dictionary from XT )
** 1 for IMMEDIATE values
*/
cell_t ffTokenToName( ExecToken XT, const ForthString **NFAPtr )
{
const ForthString *NameField;
cell_t Searching = TRUE;
cell_t Result = 0;
ExecToken TempXT;
NameField = (ForthString *) gVarContext;
DBUGX(("\ffCodeToName: gVarContext = 0x%x\n", gVarContext));
do
{
TempXT = NameToToken( NameField );
if( TempXT == XT )
{
DBUGX(("ffCodeToName: NFA = 0x%x\n", NameField));
*NFAPtr = NameField ;
Result = 1;
Searching = FALSE;
}
else
{
NameField = NameToPrevious( NameField );
if( NameField == NULL )
{
*NFAPtr = 0;
Searching = FALSE;
}
}
} while ( Searching);
return Result;
}
/*
** ( $name -- $addr 0 | nfa -1 | nfa 1 , find NFA in dictionary )
** 1 for IMMEDIATE values
*/
cell_t ffFindNFA( const ForthString *WordName, const ForthString **NFAPtr )
{
const ForthString *WordChar;
uint8_t WordLen;
const char *NameField, *NameChar;
int8_t NameLen;
cell_t Searching = TRUE;
cell_t Result = 0;
WordLen = (uint8_t) ((ucell_t)*WordName & 0x1F);
WordChar = WordName+1;
NameField = (ForthString *) gVarContext;
DBUG(("\nffFindNFA: WordLen = %d, WordName = %*s\n", WordLen, WordLen, WordChar ));
DBUG(("\nffFindNFA: gVarContext = 0x%x\n", gVarContext));
do
{
NameLen = (uint8_t) ((ucell_t)(*NameField) & MASK_NAME_SIZE);
NameChar = NameField+1;
/* DBUG((" %c\n", (*NameField & FLAG_SMUDGE) ? 'S' : 'V' )); */
if( ((*NameField & FLAG_SMUDGE) == 0) &&
(NameLen == WordLen) &&
ffCompareTextCaseN( NameChar, WordChar, WordLen ) ) /* FIXME - slow */
{
DBUG(("ffFindNFA: found it at NFA = 0x%x\n", NameField));
*NFAPtr = NameField ;
Result = ((*NameField) & FLAG_IMMEDIATE) ? 1 : -1;
Searching = FALSE;
}
else
{
NameField = NameToPrevious( NameField );
if( NameField == NULL )
{
*NFAPtr = WordName;
Searching = FALSE;
}
}
} while ( Searching);
DBUG(("ffFindNFA: returns 0x%x\n", Result));
return Result;
}
/***************************************************************
** ( $name -- $name 0 | xt -1 | xt 1 )
** 1 for IMMEDIATE values
*/
cell_t ffFind( const ForthString *WordName, ExecToken *pXT )
{
const ForthString *NFA;
cell_t Result;
Result = ffFindNFA( WordName, &NFA );
DBUG(("ffFind: %8s at 0x%x\n", WordName+1, NFA)); /* WARNING, not NUL terminated. %Q */
if( Result )
{
*pXT = NameToToken( NFA );
}
else
{
*pXT = (ExecToken) WordName;
}
return Result;
}
/****************************************************************
** Find name when passed 'C' string.
*/
cell_t ffFindC( const char *WordName, ExecToken *pXT )
{
DBUG(("ffFindC: %s\n", WordName ));
CStringToForth( gScratch, WordName, sizeof(gScratch) );
return ffFind( gScratch, pXT );
}
/***********************************************************/
/********* Compiling New Words *****************************/
/***********************************************************/
#define DIC_SAFETY_MARGIN (400)
/*************************************************************
** Check for dictionary overflow.
*/
static cell_t ffCheckDicRoom( void )
{
cell_t RoomLeft;
RoomLeft = (char *)gCurrentDictionary->dic_HeaderLimit -
(char *)gCurrentDictionary->dic_HeaderPtr;
if( RoomLeft < DIC_SAFETY_MARGIN )
{
pfReportError("ffCheckDicRoom", PF_ERR_HEADER_ROOM);
return PF_ERR_HEADER_ROOM;
}
RoomLeft = (char *)gCurrentDictionary->dic_CodeLimit -
(char *)gCurrentDictionary->dic_CodePtr.Byte;
if( RoomLeft < DIC_SAFETY_MARGIN )
{
pfReportError("ffCheckDicRoom", PF_ERR_CODE_ROOM);
return PF_ERR_CODE_ROOM;
}
return 0;
}
/*************************************************************
** Create a dictionary entry given a string name.
*/
void ffCreateSecondaryHeader( const ForthStringPtr FName)
{
pfDebugMessage("ffCreateSecondaryHeader()\n");
/* Check for dictionary overflow. */
if( ffCheckDicRoom() ) return;
pfDebugMessage("ffCreateSecondaryHeader: CheckRedefinition()\n");
CheckRedefinition( FName );
/* Align CODE_HERE */
CODE_HERE = (cell_t *)( (((ucell_t)CODE_HERE) + UINT32_MASK) & ~UINT32_MASK);
CreateDicEntry( (ExecToken) ABS_TO_CODEREL(CODE_HERE), FName, FLAG_SMUDGE );
}
/*************************************************************
** Begin compiling a secondary word.
*/
static void ffStringColon( const ForthStringPtr FName)
{
ffCreateSecondaryHeader( FName );
gVarState = 1;
}
/*************************************************************
** Read the next ExecToken from the Source and create a word.
*/
void ffColon( void )
{
char *FName;
gDepthAtColon = DATA_STACK_DEPTH;
FName = ffWord( BLANK );
if( *FName > 0 )
{
ffStringColon( FName );
}
}
/*************************************************************
** Check to see if name is already in dictionary.
*/
static cell_t CheckRedefinition( const ForthStringPtr FName )
{
cell_t flag;
ExecToken XT;
flag = ffFind( FName, &XT);
if ( flag && !gVarQuiet)
{
ioType( FName+1, (cell_t) *FName );
MSG( " redefined.\n" ); /* FIXME - allow user to run off this warning. */
}
return flag;
}
void ffStringCreate( char *FName)
{
ffCreateSecondaryHeader( FName );
CODE_COMMA( ID_CREATE_P );
CODE_COMMA( ID_EXIT );
ffFinishSecondary();
}
/* Read the next ExecToken from the Source and create a word. */
void ffCreate( void )
{
char *FName;
FName = ffWord( BLANK );
if( *FName > 0 )
{
ffStringCreate( FName );
}
}
void ffStringDefer( const ForthStringPtr FName, ExecToken DefaultXT )
{
pfDebugMessage("ffStringDefer()\n");
ffCreateSecondaryHeader( FName );
CODE_COMMA( ID_DEFER_P );
CODE_COMMA( DefaultXT );
ffFinishSecondary();
}
#ifndef PF_NO_INIT
/* Convert name then create deferred dictionary entry. */
static void CreateDeferredC( ExecToken DefaultXT, const char *CName )
{
char FName[40];
CStringToForth( FName, CName, sizeof(FName) );
ffStringDefer( FName, DefaultXT );
}
#endif
/* Read the next token from the Source and create a word. */
void ffDefer( void )
{
char *FName;
FName = ffWord( BLANK );
if( *FName > 0 )
{
ffStringDefer( FName, ID_QUIT_P );
}
}
/* Unsmudge the word to make it visible. */
static void ffUnSmudge( void )
{
*(char*)gVarContext &= ~FLAG_SMUDGE;
}
/* Implement ; */
ThrowCode ffSemiColon( void )
{
ThrowCode exception = 0;
gVarState = 0;
if( (gDepthAtColon != DATA_STACK_DEPTH) &&
(gDepthAtColon != DEPTH_AT_COLON_INVALID) ) /* Ignore if no ':' */
{
exception = THROW_SEMICOLON;
}
else
{
ffFinishSecondary();
}
gDepthAtColon = DEPTH_AT_COLON_INVALID;
return exception;
}
/* Finish the definition of a Forth word. */
void ffFinishSecondary( void )
{
CODE_COMMA( ID_EXIT );
ffUnSmudge();
}
/**************************************************************/
/* Used to pull a number from the dictionary to the stack */
void ff2Literal( cell_t dHi, cell_t dLo )
{
CODE_COMMA( ID_2LITERAL_P );
CODE_COMMA( dHi );
CODE_COMMA( dLo );
}
void ffALiteral( cell_t Num )
{
CODE_COMMA( ID_ALITERAL_P );
CODE_COMMA( Num );
}
void ffLiteral( cell_t Num )
{
CODE_COMMA( ID_LITERAL_P );
CODE_COMMA( Num );
}
#ifdef PF_SUPPORT_FP
void ffFPLiteral( PF_FLOAT fnum )
{
/* Hack for Metrowerks complier which won't compile the
* original expression.
*/
PF_FLOAT *temp;
cell_t *dicPtr;
/* Make sure that literal float data is float aligned. */
dicPtr = CODE_HERE + 1;
while( (((ucell_t) dicPtr++) & (sizeof(PF_FLOAT) - 1)) != 0)
{
DBUG((" comma NOOP to align FPLiteral\n"));
CODE_COMMA( ID_NOOP );
}
CODE_COMMA( ID_FP_FLITERAL_P );
temp = (PF_FLOAT *)CODE_HERE;
WRITE_FLOAT_DIC(temp,fnum); /* Write to dictionary. */
temp++;
CODE_HERE = (cell_t *) temp;
}
#endif /* PF_SUPPORT_FP */
/**************************************************************/
static ThrowCode FindAndCompile( const char *theWord )
{
cell_t Flag;
ExecToken XT;
cell_t Num;
ThrowCode exception = 0;
Flag = ffFind( theWord, &XT);
DBUG(("FindAndCompile: theWord = %8s, XT = 0x%x, Flag = %d\n", theWord, XT, Flag ));
/* Is it a normal word ? */
if( Flag == -1 )
{
if( gVarState ) /* compiling? */
{
CODE_COMMA( XT );
}
else
{
exception = pfCatch( XT );
}
}
else if ( Flag == 1 ) /* or is it IMMEDIATE ? */
{
DBUG(("FindAndCompile: IMMEDIATE, theWord = 0x%x\n", theWord ));
exception = pfCatch( XT );
}
else /* try to interpret it as a number. */
{
/* Call deferred NUMBER? */
cell_t NumResult;
DBUG(("FindAndCompile: not found, try number?\n" ));
PUSH_DATA_STACK( theWord ); /* Push text of number */
exception = pfCatch( gNumberQ_XT );
if( exception ) goto error;
DBUG(("FindAndCompile: after number?\n" ));
NumResult = POP_DATA_STACK; /* Success? */
switch( NumResult )
{
case NUM_TYPE_SINGLE:
if( gVarState ) /* compiling? */
{
Num = POP_DATA_STACK;
ffLiteral( Num );
}
break;
case NUM_TYPE_DOUBLE:
if( gVarState ) /* compiling? */
{
Num = POP_DATA_STACK; /* get hi portion */
ff2Literal( Num, POP_DATA_STACK );
}
break;
#ifdef PF_SUPPORT_FP
case NUM_TYPE_FLOAT:
if( gVarState ) /* compiling? */
{
ffFPLiteral( *gCurrentTask->td_FloatStackPtr++ );
}
break;
#endif
case NUM_TYPE_BAD:
default:
ioType( theWord+1, *theWord );
MSG( " ? - unrecognized word!\n" );
exception = THROW_UNDEFINED_WORD;
break;
}
}
error:
return exception;
}
/**************************************************************
** Forth outer interpreter. Parses words from Source.
** Executes them or compiles them based on STATE.
*/
ThrowCode ffInterpret( void )
{
cell_t flag;
char *theWord;
ThrowCode exception = 0;
/* Is there any text left in Source ? */
while( gCurrentTask->td_IN < (gCurrentTask->td_SourceNum) )
{
pfDebugMessage("ffInterpret: calling ffWord(()\n");
theWord = ffLWord( BLANK );
DBUG(("ffInterpret: theWord = 0x%x, Len = %d\n", theWord, *theWord ));
if( *theWord > 0 )
{
flag = 0;
if( gLocalCompiler_XT )
{
PUSH_DATA_STACK( theWord ); /* Push word. */
exception = pfCatch( gLocalCompiler_XT );
if( exception ) goto error;
flag = POP_DATA_STACK; /* Compiled local? */
}
if( flag == 0 )
{
exception = FindAndCompile( theWord );
if( exception ) goto error;
}
}
DBUG(("ffInterpret: IN=%d, SourceNum=%d\n", gCurrentTask->td_IN,
gCurrentTask->td_SourceNum ) );
}
error:
return exception;
}
/**************************************************************/
ThrowCode ffOK( void )
{
cell_t exception = 0;
/* Check for stack underflow. %Q what about overflows? */
if( (gCurrentTask->td_StackBase - gCurrentTask->td_StackPtr) < 0 )
{
exception = THROW_STACK_UNDERFLOW;
}
#ifdef PF_SUPPORT_FP /* Check floating point stack too! */
else if((gCurrentTask->td_FloatStackBase - gCurrentTask->td_FloatStackPtr) < 0)
{
exception = THROW_FLOAT_STACK_UNDERFLOW;
}
#endif
else if( gCurrentTask->td_InputStream == PF_STDIN)
{
if( !gVarState ) /* executing? */
{
if( !gVarQuiet )
{
MSG( " ok\n" );
if(gVarTraceStack) ffDotS();
}
else
{
EMIT_CR;
}
}
}
return exception;
}
/***************************************************************
** Cleanup Include stack by popping and closing files.
***************************************************************/
void pfHandleIncludeError( void )
{
FileStream *cur;
while( (cur = ffPopInputStream()) != PF_STDIN)
{
DBUG(("ffCleanIncludeStack: closing 0x%x\n", cur ));
sdCloseFile(cur);
}
}
/***************************************************************
** Interpret input in a loop.
***************************************************************/
ThrowCode ffOuterInterpreterLoop( void )
{
cell_t exception = 0;
do
{
exception = ffRefill();
if(exception <= 0) break;
exception = ffInterpret();
if( exception == 0 )
{
exception = ffOK();
}
} while( exception == 0 );
return exception;
}
/***************************************************************
** Include then close a file
***************************************************************/
ThrowCode ffIncludeFile( FileStream *InputFile )
{
ThrowCode exception;
/* Push file stream. */
exception = ffPushInputStream( InputFile );
if( exception < 0 ) return exception;
/* Run outer interpreter for stream. */
exception = ffOuterInterpreterLoop();
if( exception )
{
int i;
/* Report line number and nesting level. */
MSG("INCLUDE error on line #"); ffDot(gCurrentTask->td_LineNumber);
MSG(", level = "); ffDot(gIncludeIndex );
EMIT_CR
/* Dump line of error and show offset in line for >IN */
for( i=0; i<gCurrentTask->td_SourceNum; i++ )
{
char c = gCurrentTask->td_SourcePtr[i];
if( c == '\t' ) c = ' ';
EMIT(c);
}
EMIT_CR;
for( i=0; i<(gCurrentTask->td_IN - 1); i++ ) EMIT('^');
EMIT_CR;
}
/* Pop file stream. */
ffPopInputStream();
/* ANSI spec specifies that this should also close the file. */
sdCloseFile(InputFile);
return exception;
}
#endif /* !PF_NO_SHELL */
/***************************************************************
** Save current input stream on stack, use this new one.
***************************************************************/
Err ffPushInputStream( FileStream *InputFile )
{
Err Result = 0;
IncludeFrame *inf;
/* Push current input state onto special include stack. */
if( gIncludeIndex < MAX_INCLUDE_DEPTH )
{
inf = &gIncludeStack[gIncludeIndex++];
inf->inf_FileID = gCurrentTask->td_InputStream;
inf->inf_IN = gCurrentTask->td_IN;
inf->inf_LineNumber = gCurrentTask->td_LineNumber;
inf->inf_SourceNum = gCurrentTask->td_SourceNum;
/* Copy TIB plus any NUL terminator into saved area. */
if( (inf->inf_SourceNum > 0) && (inf->inf_SourceNum < (TIB_SIZE-1)) )
{
pfCopyMemory( inf->inf_SaveTIB, gCurrentTask->td_TIB, inf->inf_SourceNum+1 );
}
/* Set new current input. */
DBUG(( "ffPushInputStream: InputFile = 0x%x\n", InputFile ));
gCurrentTask->td_InputStream = InputFile;
gCurrentTask->td_LineNumber = 0;
}
else
{
ERR("ffPushInputStream: max depth exceeded.\n");
return -1;
}
return Result;
}
/***************************************************************
** Go back to reading previous stream.
** Just return gCurrentTask->td_InputStream upon underflow.
***************************************************************/
FileStream *ffPopInputStream( void )
{
IncludeFrame *inf;
FileStream *Result;
DBUG(("ffPopInputStream: gIncludeIndex = %d\n", gIncludeIndex));
Result = gCurrentTask->td_InputStream;
/* Restore input state. */
if( gIncludeIndex > 0 )
{
inf = &gIncludeStack[--gIncludeIndex];
gCurrentTask->td_InputStream = inf->inf_FileID;
DBUG(("ffPopInputStream: stream = 0x%x\n", gCurrentTask->td_InputStream ));
gCurrentTask->td_IN = inf->inf_IN;
gCurrentTask->td_LineNumber = inf->inf_LineNumber;
gCurrentTask->td_SourceNum = inf->inf_SourceNum;
/* Copy TIB plus any NUL terminator into saved area. */
if( (inf->inf_SourceNum > 0) && (inf->inf_SourceNum < (TIB_SIZE-1)) )
{
pfCopyMemory( gCurrentTask->td_TIB, inf->inf_SaveTIB, inf->inf_SourceNum+1 );
}
}
DBUG(("ffPopInputStream: return = 0x%x\n", Result ));
return Result;
}
/***************************************************************
** Convert file pointer to value consistent with SOURCE-ID.
***************************************************************/
cell_t ffConvertStreamToSourceID( FileStream *Stream )
{
cell_t Result;
if(Stream == PF_STDIN)
{
Result = 0;
}
else if(Stream == NULL)
{
Result = -1;
}
else
{
Result = (cell_t) Stream;
}
return Result;
}
/***************************************************************
** Convert file pointer to value consistent with SOURCE-ID.
***************************************************************/
FileStream * ffConvertSourceIDToStream( cell_t id )
{
FileStream *stream;
if( id == 0 )
{
stream = PF_STDIN;
}
else if( id == -1 )
{
stream = NULL;
}
else
{
stream = (FileStream *) id;
}
return stream;
}
/**************************************************************
** Receive line from input stream.
** Return length, or -1 for EOF.
*/
#define BACKSPACE (8)
static cell_t readLineFromStream( char *buffer, cell_t maxChars, FileStream *stream )
{
int c;
int len;
char *p;
static int lastChar = 0;
int done = 0;
DBUGX(("readLineFromStream(0x%x, 0x%x, 0x%x)\n", buffer, len, stream ));
p = buffer;
len = 0;
while( (len < maxChars) && !done )
{
c = sdInputChar(stream);
switch(c)
{
case EOF:
DBUG(("EOF\n"));
done = 1;
if( len <= 0 ) len = -1;
break;
case '\n':
DBUGX(("EOL=\\n\n"));
if( lastChar != '\r' ) done = 1;
break;
case '\r':
DBUGX(("EOL=\\r\n"));
done = 1;
break;
default:
*p++ = (char) c;
len++;
break;
}
lastChar = c;
}
/* NUL terminate line to simplify printing when debugging. */
if( (len >= 0) && (len < maxChars) ) p[len] = '\0';
return len;
}
/**************************************************************
** ( -- , fill Source from current stream )
** Return 1 if successful, 0 for EOF, or a negative error.
*/
cell_t ffRefill( void )
{
cell_t Num;
cell_t Result = 1;
/* reset >IN for parser */
gCurrentTask->td_IN = 0;
/* get line from current stream */
if( gCurrentTask->td_InputStream == PF_STDIN )
{
/* ACCEPT is deferred so we call it through the dictionary. */
PUSH_DATA_STACK( gCurrentTask->td_SourcePtr );
PUSH_DATA_STACK( TIB_SIZE );
ThrowCode throwCode = pfCatch( gAcceptP_XT );
if (throwCode) {
Result = throwCode;
goto error;
}
Num = POP_DATA_STACK;
if( Num < 0 )
{
Result = Num;
goto error;
}
}
else
{
Num = readLineFromStream( gCurrentTask->td_SourcePtr, TIB_SIZE,
gCurrentTask->td_InputStream );
if( Num == EOF )
{
Result = 0;
Num = 0;
}
}
gCurrentTask->td_SourceNum = Num;
gCurrentTask->td_LineNumber++; /* Bump for include. */
/* echo input if requested */
if( gVarEcho && ( Num > 0))
{
ioType( gCurrentTask->td_SourcePtr, gCurrentTask->td_SourceNum );
EMIT_CR;
}
error:
return Result;
}