ref: 7d02e382d314d5bdde7978ccb7a64ea9201d03db
dir: /vm.c/
/*******************************************************************
** v m . c
** Forth Inspired Command Language - virtual machine methods
** Author: John Sadler (john_sadler@alum.mit.edu)
** Created: 19 July 1997
** $Id: vm.c,v 1.22 2010/12/22 09:05:52 asau Exp $
*******************************************************************/
/*
** This file implements the virtual machine of Ficl. Each virtual
** machine retains the state of an interpreter. A virtual machine
** owns a pair of stacks for parameters and return addresses, as
** well as a pile of state variables and the two dedicated registers
** of the interpreter.
*/
/*
** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
** All rights reserved.
**
** Get the latest Ficl release at http://ficl.sourceforge.net
**
** I am interested in hearing from anyone who uses Ficl. If you have
** a problem, a success story, a defect, an enhancement request, or
** if you would like to contribute to the Ficl release, please
** contact me by email at the address above.
**
** L I C E N S E and D I S C L A I M E R
**
** 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.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "ficl.h"
#if FICL_ROBUST >= 2
#define FICL_VM_CHECK(vm) FICL_VM_ASSERT(vm, (*(vm->ip - 1)) == vm->runningWord)
#else
#define FICL_VM_CHECK(vm)
#endif
/**************************************************************************
v m B r a n c h R e l a t i v e
**
**************************************************************************/
void ficlVmBranchRelative(ficlVm *vm, int offset)
{
vm->ip += offset;
return;
}
/**************************************************************************
v m C r e a t e
** Creates a virtual machine either from scratch (if vm is NULL on entry)
** or by resizing and reinitializing an existing VM to the specified stack
** sizes.
**************************************************************************/
ficlVm *ficlVmCreate(ficlVm *vm, unsigned nPStack, unsigned nRStack)
{
if (vm == NULL)
{
vm = (ficlVm *)ficlMalloc(sizeof (ficlVm));
FICL_ASSERT(NULL, vm);
memset(vm, 0, sizeof (ficlVm));
}
if (vm->dataStack)
ficlStackDestroy(vm->dataStack);
vm->dataStack = ficlStackCreate(vm, "data", nPStack);
if (vm->returnStack)
ficlStackDestroy(vm->returnStack);
vm->returnStack = ficlStackCreate(vm, "return", nRStack);
#if FICL_WANT_FLOAT
if (vm->floatStack)
ficlStackDestroy(vm->floatStack);
vm->floatStack = ficlStackCreate(vm, "float", nPStack);
#endif
ficlVmReset(vm);
return vm;
}
/**************************************************************************
v m D e l e t e
** Free all memory allocated to the specified VM and its subordinate
** structures.
**************************************************************************/
void ficlVmDestroy(ficlVm *vm)
{
if (vm)
{
ficlFree(vm->dataStack);
ficlFree(vm->returnStack);
#if FICL_WANT_FLOAT
ficlFree(vm->floatStack);
#endif
ficlFree(vm);
}
return;
}
/**************************************************************************
v m E x e c u t e
** Sets up the specified word to be run by the inner interpreter.
** Executes the word's code part immediately, but in the case of
** colon definition, the definition itself needs the inner interpreter
** to complete. This does not happen until control reaches ficlExec
**************************************************************************/
void ficlVmExecuteWord(ficlVm *vm, ficlWord *pWord)
{
ficlVmInnerLoop(vm, pWord);
return;
}
static void ficlVmOptimizeJumpToJump(ficlVm *vm, ficlIp ip)
{
ficlIp destination;
switch ((ficlInstruction)(*ip))
{
case ficlInstructionBranchParenWithCheck:
*ip = (ficlWord *)ficlInstructionBranchParen;
goto RUNTIME_FIXUP;
case ficlInstructionBranch0ParenWithCheck:
*ip = (ficlWord *)ficlInstructionBranch0Paren;
RUNTIME_FIXUP:
ip++;
destination = ip + *(int *)ip;
switch ((ficlInstruction)*destination)
{
case ficlInstructionBranchParenWithCheck:
/* preoptimize where we're jumping to */
ficlVmOptimizeJumpToJump(vm, destination);
case ficlInstructionBranchParen:
{
destination++;
destination += *(int *)destination;
*ip = (ficlWord *)(destination - ip);
break;
}
}
}
}
/**************************************************************************
v m I n n e r L o o p
** the mysterious inner interpreter...
** This loop is the address interpreter that makes colon definitions
** work. Upon entry, it assumes that the IP points to an entry in
** a definition (the body of a colon word). It runs one word at a time
** until something does vmThrow. The catcher for this is expected to exist
** in the calling code.
** vmThrow gets you out of this loop with a longjmp()
**************************************************************************/
#if FICL_ROBUST <= 1
/* turn off stack checking for primitives */
#define _CHECK_STACK(stack, top, pop, push)
#else
#define _CHECK_STACK(stack, top, pop, push) \
ficlStackCheckNospill(stack, top, pop, push)
FICL_PLATFORM_INLINE void ficlStackCheckNospill(ficlStack *stack, ficlCell *top, int popCells, int pushCells)
{
/*
** Why save and restore stack->top?
** So the simple act of stack checking doesn't force a "register" spill,
** which might mask bugs (places where we needed to spill but didn't).
** --lch
*/
ficlCell *oldTop = stack->top;
stack->top = top;
ficlStackCheck(stack, popCells, pushCells);
stack->top = oldTop;
}
#endif /* FICL_ROBUST <= 1 */
#define CHECK_STACK(pop, push) _CHECK_STACK(vm->dataStack, dataTop, pop, push)
#define CHECK_FLOAT_STACK(pop, push) _CHECK_STACK(vm->floatStack, floatTop, pop, push)
#define CHECK_RETURN_STACK(pop, push) _CHECK_STACK(vm->returnStack, returnTop, pop, push)
#if FICL_WANT_FLOAT
#define FLOAT_LOCAL_VARIABLE_SPILL \
vm->floatStack->top = floatTop;
#define FLOAT_LOCAL_VARIABLE_REFILL \
floatTop = vm->floatStack->top;
#else
#define FLOAT_LOCAL_VARIABLE_SPILL
#define FLOAT_LOCAL_VARIABLE_REFILL
#endif /* FICL_WANT_FLOAT */
#if FICL_WANT_LOCALS
#define LOCALS_LOCAL_VARIABLE_SPILL \
vm->returnStack->frame = frame;
#define LOCALS_LOCAL_VARIABLE_REFILL \
frame = vm->returnStack->frame;
#else
#define LOCALS_LOCAL_VARIABLE_SPILL
#define LOCALS_LOCAL_VARIABLE_REFILL
#endif /* FICL_WANT_FLOAT */
#define LOCAL_VARIABLE_SPILL \
vm->ip = (ficlIp)ip; \
vm->dataStack->top = dataTop; \
vm->returnStack->top = returnTop; \
FLOAT_LOCAL_VARIABLE_SPILL \
LOCALS_LOCAL_VARIABLE_SPILL
#define LOCAL_VARIABLE_REFILL \
ip = (ficlInstruction *)vm->ip; \
dataTop = vm->dataStack->top; \
returnTop = vm->returnStack->top; \
FLOAT_LOCAL_VARIABLE_REFILL \
LOCALS_LOCAL_VARIABLE_REFILL
void ficlVmInnerLoop(ficlVm *vm, ficlWord *fw)
{
register ficlInstruction *ip;
register ficlCell *dataTop;
register ficlCell *returnTop;
#if FICL_WANT_FLOAT
register ficlCell *floatTop;
#endif /* FICL_WANT_FLOAT */
#if FICL_WANT_LOCALS
register ficlCell *frame;
#endif /* FICL_WANT_LOCALS */
jmp_buf *oldExceptionHandler;
jmp_buf exceptionHandler;
int except;
int once;
int count;
ficlInstruction instruction;
ficlInteger i;
ficlUnsigned u;
ficlCell c;
ficlCountedString *s;
ficlCell *cell;
char *cp;
once = (fw != NULL);
if (once)
count = 1;
LOCAL_VARIABLE_REFILL;
oldExceptionHandler = vm->exceptionHandler;
vm->exceptionHandler = &exceptionHandler; /* This has to come before the setjmp! */
except = setjmp(exceptionHandler);
if (except)
{
LOCAL_VARIABLE_SPILL;
vm->exceptionHandler = oldExceptionHandler;
ficlVmThrow(vm, except);
}
for (;;)
{
if (once)
{
if (!count--)
break;
instruction = (ficlInstruction)((void *)fw);
}
else
{
instruction = *ip++;
fw = (ficlWord *)instruction;
}
AGAIN:
switch (instruction)
{
case ficlInstructionInvalid:
{
ficlVmThrowError(vm, "Error: NULL instruction executed!");
return;
}
case ficlInstruction1:
case ficlInstruction2:
case ficlInstruction3:
case ficlInstruction4:
case ficlInstruction5:
case ficlInstruction6:
case ficlInstruction7:
case ficlInstruction8:
case ficlInstruction9:
case ficlInstruction10:
case ficlInstruction11:
case ficlInstruction12:
case ficlInstruction13:
case ficlInstruction14:
case ficlInstruction15:
case ficlInstruction16:
{
CHECK_STACK(0, 1);
(++dataTop)->i = instruction;
break;
}
case ficlInstruction0:
case ficlInstructionNeg1:
case ficlInstructionNeg2:
case ficlInstructionNeg3:
case ficlInstructionNeg4:
case ficlInstructionNeg5:
case ficlInstructionNeg6:
case ficlInstructionNeg7:
case ficlInstructionNeg8:
case ficlInstructionNeg9:
case ficlInstructionNeg10:
case ficlInstructionNeg11:
case ficlInstructionNeg12:
case ficlInstructionNeg13:
case ficlInstructionNeg14:
case ficlInstructionNeg15:
case ficlInstructionNeg16:
{
CHECK_STACK(0, 1);
(++dataTop)->i = ficlInstruction0 - instruction;
break;
}
/**************************************************************************
** stringlit: Fetch the count from the dictionary, then push the address
** and count on the stack. Finally, update ip to point to the first
** aligned address after the string text.
**************************************************************************/
case ficlInstructionStringLiteralParen:
{
ficlUnsigned8 length;
CHECK_STACK(0, 2);
s = (ficlCountedString *)(ip);
length = s->length;
cp = s->text;
(++dataTop)->p = cp;
(++dataTop)->i = length;
cp += length + 1;
cp = (char *)ficlAlignPointer(cp);
ip = (ficlInstruction *)cp;
break;
}
case ficlInstructionCStringLiteralParen:
{
CHECK_STACK(0, 1);
s = (ficlCountedString *)(ip);
cp = s->text + s->length + 1;
cp = (char *)ficlAlignPointer(cp);
ip = (ficlInstruction *)cp;
(++dataTop)->p = s;
break;
}
#if FICL_WANT_OPTIMIZE == FICL_OPTIMIZE_FOR_SIZE
#if FICL_WANT_FLOAT
FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC:
*++floatTop = cell[1];
/* intentional fall-through */
FLOAT_PUSH_CELL_POINTER_MINIPROC:
*++floatTop = cell[0];
break;
FLOAT_POP_CELL_POINTER_MINIPROC:
cell[0] = *floatTop--;
break;
FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC:
cell[0] = *floatTop--;
cell[1] = *floatTop--;
break;
#define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) cell = (cp); goto FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC
#define FLOAT_PUSH_CELL_POINTER(cp) cell = (cp); goto FLOAT_PUSH_CELL_POINTER_MINIPROC
#define FLOAT_POP_CELL_POINTER_DOUBLE(cp) cell = (cp); goto FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC
#define FLOAT_POP_CELL_POINTER(cp) cell = (cp); goto FLOAT_POP_CELL_POINTER_MINIPROC
#endif /* FICL_WANT_FLOAT */
/*
** Think of these as little mini-procedures.
** --lch
*/
PUSH_CELL_POINTER_DOUBLE_MINIPROC:
*++dataTop = cell[1];
/* intentional fall-through */
PUSH_CELL_POINTER_MINIPROC:
*++dataTop = cell[0];
break;
POP_CELL_POINTER_MINIPROC:
cell[0] = *dataTop--;
break;
POP_CELL_POINTER_DOUBLE_MINIPROC:
cell[0] = *dataTop--;
cell[1] = *dataTop--;
break;
#define PUSH_CELL_POINTER_DOUBLE(cp) cell = (cp); goto PUSH_CELL_POINTER_DOUBLE_MINIPROC
#define PUSH_CELL_POINTER(cp) cell = (cp); goto PUSH_CELL_POINTER_MINIPROC
#define POP_CELL_POINTER_DOUBLE(cp) cell = (cp); goto POP_CELL_POINTER_DOUBLE_MINIPROC
#define POP_CELL_POINTER(cp) cell = (cp); goto POP_CELL_POINTER_MINIPROC
BRANCH_MINIPROC:
ip += *(ficlInstruction *)ip;
break;
#define BRANCH() goto BRANCH_MINIPROC
EXIT_FUNCTION_MINIPROC:
ip = (ficlInstruction *)((returnTop--)->p);
break;
#define EXIT_FUNCTION goto EXIT_FUNCTION_MINIPROC
#else /* FICL_WANT_SIZE */
#if FICL_WANT_FLOAT
#define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) cell = (cp); *++floatTop = cell[1]; *++floatTop = *cell; break
#define FLOAT_PUSH_CELL_POINTER(cp) cell = (cp); *++floatTop = *cell; break
#define FLOAT_POP_CELL_POINTER_DOUBLE(cp) cell = (cp); *cell = *floatTop--; cell[1] = *floatTop--; break
#define FLOAT_POP_CELL_POINTER(cp) cell = (cp); *cell = *floatTop--; break
#endif /* FICL_WANT_FLOAT */
#define PUSH_CELL_POINTER_DOUBLE(cp) cell = (cp); *++dataTop = cell[1]; *++dataTop = *cell; break
#define PUSH_CELL_POINTER(cp) cell = (cp); *++dataTop = *cell; break
#define POP_CELL_POINTER_DOUBLE(cp) cell = (cp); *cell = *dataTop--; cell[1] = *dataTop--; break
#define POP_CELL_POINTER(cp) cell = (cp); *cell = *dataTop--; break
#define BRANCH() ip += *(ficlInteger *)ip; break
#define EXIT_FUNCTION() ip = (ficlInstruction *)((returnTop--)->p); break
#endif /* FICL_WANT_SIZE */
/**************************************************************************
** This is the runtime for (literal). It assumes that it is part of a colon
** definition, and that the next ficlCell contains a value to be pushed on the
** parameter stack at runtime. This code is compiled by "literal".
**************************************************************************/
case ficlInstructionLiteralParen:
{
CHECK_STACK(0, 1);
(++dataTop)->i = *ip++;
break;
}
case ficlInstruction2LiteralParen:
{
CHECK_STACK(0, 2);
(++dataTop)->i = ip[1];
(++dataTop)->i = ip[0];
ip += 2;
break;
}
#if FICL_WANT_LOCALS
/**************************************************************************
** Link a frame on the return stack, reserving nCells of space for
** locals - the value of nCells is the next ficlCell in the instruction
** stream.
** 1) Push frame onto returnTop
** 2) frame = returnTop
** 3) returnTop += nCells
**************************************************************************/
case ficlInstructionLinkParen:
{
ficlInteger nCells = *ip++;
(++returnTop)->p = frame;
frame = returnTop + 1;
returnTop += nCells;
break;
}
/**************************************************************************
** Unink a stack frame previously created by stackLink
** 1) dataTop = frame
** 2) frame = pop()
*******************************************************************/
case ficlInstructionUnlinkParen:
{
returnTop = frame - 1;
frame = (ficlCell *)(returnTop--)->p;
break;
}
/**************************************************************************
** Immediate - cfa of a local while compiling - when executed, compiles
** code to fetch the value of a local given the local's index in the
** word's pfa
**************************************************************************/
#if FICL_WANT_FLOAT
case ficlInstructionGetF2LocalParen:
FLOAT_PUSH_CELL_POINTER_DOUBLE(frame + *ip++);
case ficlInstructionGetFLocalParen:
FLOAT_PUSH_CELL_POINTER(frame + *ip++);
case ficlInstructionToF2LocalParen:
FLOAT_POP_CELL_POINTER_DOUBLE(frame + *ip++);
case ficlInstructionToFLocalParen:
FLOAT_POP_CELL_POINTER(frame + *ip++);
#endif /* FICL_WANT_FLOAT */
case ficlInstructionGet2LocalParen:
PUSH_CELL_POINTER_DOUBLE(frame + *ip++);
case ficlInstructionGetLocalParen:
PUSH_CELL_POINTER(frame + *ip++);
/**************************************************************************
** Immediate - cfa of a local while compiling - when executed, compiles
** code to store the value of a local given the local's index in the
** word's pfa
**************************************************************************/
case ficlInstructionTo2LocalParen:
POP_CELL_POINTER_DOUBLE(frame + *ip++);
case ficlInstructionToLocalParen:
POP_CELL_POINTER(frame + *ip++);
/*
** Silly little minor optimizations.
** --lch
*/
case ficlInstructionGetLocal0:
PUSH_CELL_POINTER(frame);
case ficlInstructionGetLocal1:
PUSH_CELL_POINTER(frame + 1);
case ficlInstructionGet2Local0:
PUSH_CELL_POINTER_DOUBLE(frame);
case ficlInstructionToLocal0:
POP_CELL_POINTER(frame);
case ficlInstructionToLocal1:
POP_CELL_POINTER(frame + 1);
case ficlInstructionTo2Local0:
POP_CELL_POINTER_DOUBLE(frame);
#endif /* FICL_WANT_LOCALS */
case ficlInstructionPlus:
{
CHECK_STACK(2, 1);
i = (dataTop--)->i;
dataTop->i += i;
break;
}
case ficlInstructionMinus:
{
CHECK_STACK(2, 1);
i = (dataTop--)->i;
dataTop->i -= i;
break;
}
case ficlInstruction1Plus:
{
CHECK_STACK(1, 1);
dataTop->i++;
break;
}
case ficlInstruction1Minus:
{
CHECK_STACK(1, 1);
dataTop->i--;
break;
}
case ficlInstruction2Plus:
{
CHECK_STACK(1, 1);
dataTop->i += 2;
break;
}
case ficlInstruction2Minus:
{
CHECK_STACK(1, 1);
dataTop->i -= 2;
break;
}
case ficlInstructionDup:
{
ficlInteger i = dataTop->i;
CHECK_STACK(0, 1);
(++dataTop)->i = i;
break;
}
case ficlInstructionQuestionDup:
{
CHECK_STACK(1, 2);
if (dataTop->i != 0)
{
dataTop[1] = dataTop[0];
dataTop++;
}
break;
}
case ficlInstructionSwap:
{
ficlCell swap;
CHECK_STACK(2, 2);
swap = dataTop[0];
dataTop[0] = dataTop[-1];
dataTop[-1] = swap;
break;
}
case ficlInstructionDrop:
{
CHECK_STACK(1, 0);
dataTop--;
break;
}
case ficlInstruction2Drop:
{
CHECK_STACK(2, 0);
dataTop -= 2;
break;
}
case ficlInstruction2Dup:
{
CHECK_STACK(2, 4);
dataTop[1] = dataTop[-1];
dataTop[2] = *dataTop;
dataTop += 2;
break;
}
case ficlInstructionOver:
{
CHECK_STACK(2, 3);
dataTop[1] = dataTop[-1];
dataTop++;
break;
}
case ficlInstruction2Over:
{
CHECK_STACK(4, 6);
dataTop[1] = dataTop[-3];
dataTop[2] = dataTop[-2];
dataTop += 2;
break;
}
case ficlInstructionPick:
{
CHECK_STACK(1, 0);
i = dataTop->i;
if (i < 0)
break;
CHECK_STACK(i + 1, i + 2);
*dataTop = dataTop[-i];
break;
}
/*******************************************************************
** Do stack rot.
** rot ( 1 2 3 -- 2 3 1 )
*******************************************************************/
case ficlInstructionRot:
{
i = 2;
goto ROLL;
}
/*******************************************************************
** Do stack roll.
** roll ( n -- )
*******************************************************************/
case ficlInstructionRoll:
{
CHECK_STACK(1, 0);
i = (dataTop--)->i;
if (i < 1)
break;
ROLL:
CHECK_STACK(i+1, i+2);
c = dataTop[-i];
memmove(dataTop - i, dataTop - (i - 1), i * sizeof(ficlCell));
*dataTop = c;
break;
}
/*******************************************************************
** Do stack -rot.
** -rot ( 1 2 3 -- 3 1 2 )
*******************************************************************/
case ficlInstructionMinusRot:
{
i = 2;
goto MINUSROLL;
}
/*******************************************************************
** Do stack -roll.
** -roll ( n -- )
*******************************************************************/
case ficlInstructionMinusRoll:
{
CHECK_STACK(1, 0);
i = (dataTop--)->i;
if (i < 1)
break;
MINUSROLL:
CHECK_STACK(i+1, i+2);
c = *dataTop;
memmove(dataTop - (i - 1), dataTop - i, i * sizeof(ficlCell));
dataTop[-i] = c;
break;
}
/*******************************************************************
** Do stack 2swap
** 2swap ( 1 2 3 4 -- 3 4 1 2 )
*******************************************************************/
case ficlInstruction2Swap:
{
ficlCell c2;
CHECK_STACK(4, 4);
c = *dataTop;
c2 = dataTop[-1];
*dataTop = dataTop[-2];
dataTop[-1] = dataTop[-3];
dataTop[-2] = c;
dataTop[-3] = c2;
break;
}
case ficlInstructionPlusStore:
{
ficlCell *cell;
CHECK_STACK(2, 0);
cell = (ficlCell *)(dataTop--)->p;
cell->i += (dataTop--)->i;
break;
}
case ficlInstructionQuadFetch:
{
ficlUnsigned32 *integer32;
CHECK_STACK(1, 1);
integer32 = (ficlUnsigned32 *)dataTop->i;
dataTop->u = (ficlUnsigned)*integer32;
break;
}
case ficlInstructionQuadStore:
{
ficlUnsigned32 *integer32;
CHECK_STACK(2, 0);
integer32 = (ficlUnsigned32 *)(dataTop--)->p;
*integer32 = (ficlUnsigned32)((dataTop--)->u);
break;
}
case ficlInstructionWFetch:
{
ficlUnsigned16 *integer16;
CHECK_STACK(1, 1);
integer16 = (ficlUnsigned16 *)dataTop->p;
dataTop->u = ((ficlUnsigned)*integer16);
break;
}
case ficlInstructionWStore:
{
ficlUnsigned16 *integer16;
CHECK_STACK(2, 0);
integer16 = (ficlUnsigned16 *)(dataTop--)->p;
*integer16 = (ficlUnsigned16)((dataTop--)->u);
break;
}
case ficlInstructionCFetch:
{
ficlUnsigned8 *integer8;
CHECK_STACK(1, 1);
integer8 = (ficlUnsigned8 *)dataTop->p;
dataTop->u = ((ficlUnsigned)*integer8);
break;
}
case ficlInstructionCStore:
{
ficlUnsigned8 *integer8;
CHECK_STACK(2, 0);
integer8 = (ficlUnsigned8 *)(dataTop--)->p;
*integer8 = (ficlUnsigned8)((dataTop--)->u);
break;
}
/**************************************************************************
l o g i c a n d c o m p a r i s o n s
**
**************************************************************************/
case ficlInstruction0Equals:
{
CHECK_STACK(1, 1);
dataTop->i = FICL_BOOL(dataTop->i == 0);
break;
}
case ficlInstruction0Less:
{
CHECK_STACK(1, 1);
dataTop->i = FICL_BOOL(dataTop->i < 0);
break;
}
case ficlInstruction0Greater:
{
CHECK_STACK(1, 1);
dataTop->i = FICL_BOOL(dataTop->i > 0);
break;
}
case ficlInstructionEquals:
{
CHECK_STACK(2, 1);
i = (dataTop--)->i;
dataTop->i = FICL_BOOL(dataTop->i == i);
break;
}
case ficlInstructionLess:
{
CHECK_STACK(2, 1);
i = (dataTop--)->i;
dataTop->i = FICL_BOOL(dataTop->i < i);
break;
}
case ficlInstructionULess:
{
CHECK_STACK(2, 1);
u = (dataTop--)->u;
dataTop->i = FICL_BOOL(dataTop->u < u);
break;
}
case ficlInstructionAnd:
{
CHECK_STACK(2, 1);
i = (dataTop--)->i;
dataTop->i = dataTop->i & i;
break;
}
case ficlInstructionOr:
{
CHECK_STACK(2, 1);
i = (dataTop--)->i;
dataTop->i = dataTop->i | i;
break;
}
case ficlInstructionXor:
{
CHECK_STACK(2, 1);
i = (dataTop--)->i;
dataTop->i = dataTop->i ^ i;
break;
}
case ficlInstructionInvert:
{
CHECK_STACK(1, 1);
dataTop->i = ~dataTop->i;
break;
}
/**************************************************************************
r e t u r n s t a c k
**
**************************************************************************/
case ficlInstructionToRStack:
{
CHECK_STACK(1, 0);
CHECK_RETURN_STACK(0, 1);
*++returnTop = *dataTop--;
break;
}
case ficlInstructionFromRStack:
{
CHECK_STACK(0, 1);
CHECK_RETURN_STACK(1, 0);
*++dataTop = *returnTop--;
break;
}
case ficlInstructionFetchRStack:
{
CHECK_STACK(0, 1);
CHECK_RETURN_STACK(1, 1);
*++dataTop = *returnTop;
break;
}
case ficlInstruction2ToR:
{
CHECK_STACK(2, 0);
CHECK_RETURN_STACK(0, 2);
*++returnTop = dataTop[-1];
*++returnTop = dataTop[0];
dataTop -= 2;
break;
}
case ficlInstruction2RFrom:
{
CHECK_STACK(0, 2);
CHECK_RETURN_STACK(2, 0);
*++dataTop = returnTop[-1];
*++dataTop = returnTop[0];
returnTop -= 2;
break;
}
case ficlInstruction2RFetch:
{
CHECK_STACK(0, 2);
CHECK_RETURN_STACK(2, 2);
*++dataTop = returnTop[-1];
*++dataTop = returnTop[0];
break;
}
/**************************************************************************
f i l l
** CORE ( c-addr u char -- )
** If u is greater than zero, store char in each of u consecutive
** characters of memory beginning at c-addr.
**************************************************************************/
case ficlInstructionFill:
{
char c;
char *memory;
CHECK_STACK(3, 0);
c = (char)(dataTop--)->i;
u = (dataTop--)->u;
memory = (char *)(dataTop--)->p;
/* memset() is faster than the previous hand-rolled solution. --lch */
memset(memory, c, u);
break;
}
/**************************************************************************
l s h i f t
** l-shift CORE ( x1 u -- x2 )
** Perform a logical left shift of u bit-places on x1, giving x2.
** Put zeroes into the least significant bits vacated by the shift.
** An ambiguous condition exists if u is greater than or equal to the
** number of bits in a ficlCell.
**
** r-shift CORE ( x1 u -- x2 )
** Perform a logical right shift of u bit-places on x1, giving x2.
** Put zeroes into the most significant bits vacated by the shift. An
** ambiguous condition exists if u is greater than or equal to the
** number of bits in a ficlCell.
**************************************************************************/
case ficlInstructionLShift:
{
ficlUnsigned nBits;
ficlUnsigned x1;
CHECK_STACK(2, 1);
nBits = (dataTop--)->u;
x1 = dataTop->u;
dataTop->u = x1 << nBits;
break;
}
case ficlInstructionRShift:
{
ficlUnsigned nBits;
ficlUnsigned x1;
CHECK_STACK(2, 1);
nBits = (dataTop--)->u;
x1 = dataTop->u;
dataTop->u = x1 >> nBits;
break;
}
/**************************************************************************
m a x & m i n
**
**************************************************************************/
case ficlInstructionMax:
{
ficlInteger n2;
ficlInteger n1;
CHECK_STACK(2, 1);
n2 = (dataTop--)->i;
n1 = dataTop->i;
dataTop->i = ((n1 > n2) ? n1 : n2);
break;
}
case ficlInstructionMin:
{
ficlInteger n2;
ficlInteger n1;
CHECK_STACK(2, 1);
n2 = (dataTop--)->i;
n1 = dataTop->i;
dataTop->i = ((n1 < n2) ? n1 : n2);
break;
}
/**************************************************************************
m o v e
** CORE ( addr1 addr2 u -- )
** If u is greater than zero, copy the contents of u consecutive address
** units at addr1 to the u consecutive address units at addr2. After MOVE
** completes, the u consecutive address units at addr2 contain exactly
** what the u consecutive address units at addr1 contained before the move.
** NOTE! This implementation assumes that a char is the same size as
** an address unit.
**************************************************************************/
case ficlInstructionMove:
{
ficlUnsigned u;
char *addr2;
char *addr1;
CHECK_STACK(3, 0);
u = (dataTop--)->u;
addr2 = (char *)(dataTop--)->p;
addr1 = (char *)(dataTop--)->p;
if (u == 0)
break;
/*
** Do the copy carefully, so as to be
** correct even if the two ranges overlap
*/
/* Which ANSI C's memmove() does for you! Yay! --lch */
memmove(addr2, addr1, u);
break;
}
/**************************************************************************
s t o d
** s-to-d CORE ( n -- d )
** Convert the number n to the double-ficlCell number d with the same
** numerical value.
**************************************************************************/
case ficlInstructionSToD:
{
ficlInteger s;
CHECK_STACK(1, 2);
s = dataTop->i;
/* sign extend to 64 bits.. */
(++dataTop)->i = (s < 0) ? -1 : 0;
break;
}
/**************************************************************************
c o m p a r e
** STRING ( c-addr1 u1 c-addr2 u2 -- n )
** Compare the string specified by c-addr1 u1 to the string specified by
** c-addr2 u2. The strings are compared, beginning at the given addresses,
** character by character, up to the length of the shorter string or until a
** difference is found. If the two strings are identical, n is zero. If the two
** strings are identical up to the length of the shorter string, n is minus-one
** (-1) if u1 is less than u2 and one (1) otherwise. If the two strings are not
** identical up to the length of the shorter string, n is minus-one (-1) if the
** first non-matching character in the string specified by c-addr1 u1 has a
** lesser numeric value than the corresponding character in the string specified
** by c-addr2 u2 and one (1) otherwise.
**************************************************************************/
case ficlInstructionCompare:
{
i = FICL_FALSE;
goto COMPARE;
}
case ficlInstructionCompareInsensitive:
{
i = FICL_TRUE;
goto COMPARE;
}
COMPARE:
{
char *cp1, *cp2;
ficlUnsigned u1, u2, uMin;
int n = 0;
CHECK_STACK(4, 1);
u2 = (dataTop--)->u;
cp2 = (char *)(dataTop--)->p;
u1 = (dataTop--)->u;
cp1 = (char *)(dataTop--)->p;
uMin = (u1 < u2)? u1 : u2;
for ( ; (uMin > 0) && (n == 0); uMin--)
{
int c1 = (unsigned char)*cp1++;
int c2 = (unsigned char)*cp2++;
if (i)
{
c1 = tolower(c1);
c2 = tolower(c2);
}
n = (c1 - c2);
}
if (n == 0)
n = (int)(u1 - u2);
if (n < 0)
n = -1;
else if (n > 0)
n = 1;
(++dataTop)->i = n;
break;
}
/**************************************************************************
** r a n d o m
** Ficl-specific
**************************************************************************/
case ficlInstructionRandom:
{
(++dataTop)->i = rand();
break;
}
/**************************************************************************
** s e e d - r a n d o m
** Ficl-specific
**************************************************************************/
case ficlInstructionSeedRandom:
{
srand((dataTop--)->i);
break;
}
case ficlInstructionGreaterThan:
{
ficlInteger x, y;
CHECK_STACK(2, 1);
y = (dataTop--)->i;
x = dataTop->i;
dataTop->i = FICL_BOOL(x > y);
break;
}
/**************************************************************************
** This function simply pops the previous instruction
** pointer and returns to the "next" loop. Used for exiting from within
** a definition. Note that exitParen is identical to semiParen - they
** are in two different functions so that "see" can correctly identify
** the end of a colon definition, even if it uses "exit".
**************************************************************************/
case ficlInstructionExitParen:
case ficlInstructionSemiParen:
EXIT_FUNCTION();
/**************************************************************************
** The first time we run "(branch)", perform a "peephole optimization" to
** see if we're jumping to another unconditional jump. If so, just jump
** directly there.
**************************************************************************/
case ficlInstructionBranchParenWithCheck:
{
LOCAL_VARIABLE_SPILL;
ficlVmOptimizeJumpToJump(vm, vm->ip - 1);
LOCAL_VARIABLE_REFILL;
goto BRANCH_PAREN;
}
/**************************************************************************
** Same deal with branch0.
**************************************************************************/
case ficlInstructionBranch0ParenWithCheck:
{
LOCAL_VARIABLE_SPILL;
ficlVmOptimizeJumpToJump(vm, vm->ip - 1);
LOCAL_VARIABLE_REFILL;
/* intentional fall-through */
}
/**************************************************************************
** Runtime code for "(branch0)"; pop a flag from the stack,
** branch if 0. fall through otherwise. The heart of "if" and "until".
**************************************************************************/
case ficlInstructionBranch0Paren:
{
CHECK_STACK(1, 0);
if ((dataTop--)->i)
{
/* don't branch, but skip over branch relative address */
ip += 1;
break;
}
/* otherwise, take branch (to else/endif/begin) */
/* intentional fall-through! */
}
/**************************************************************************
** Runtime for "(branch)" -- expects a literal offset in the next
** compilation address, and branches to that location.
**************************************************************************/
case ficlInstructionBranchParen:
{
BRANCH_PAREN:
BRANCH();
}
case ficlInstructionOfParen:
{
ficlUnsigned a, b;
CHECK_STACK(2, 1);
a = (dataTop--)->u;
b = dataTop->u;
if (a == b)
{
/* fall through */
ip++;
/* remove CASE argument */
dataTop--;
}
else
{
/* take branch to next of or endcase */
BRANCH();
}
break;
}
case ficlInstructionDoParen:
{
ficlCell index, limit;
CHECK_STACK(2, 0);
index = *dataTop--;
limit = *dataTop--;
/* copy "leave" target addr to stack */
(++returnTop)->i = *(ip++);
*++returnTop = limit;
*++returnTop = index;
break;
}
case ficlInstructionQDoParen:
{
ficlCell index, limit, leave;
CHECK_STACK(2, 0);
index = *dataTop--;
limit = *dataTop--;
leave.i = *ip;
if (limit.u == index.u)
{
ip = (ficlInstruction *)leave.p;
}
else
{
ip++;
*++returnTop = leave;
*++returnTop = limit;
*++returnTop = index;
}
break;
}
case ficlInstructionLoopParen:
case ficlInstructionPlusLoopParen:
{
ficlInteger index;
ficlInteger limit;
int direction = 0;
index = returnTop->i;
limit = returnTop[-1].i;
if (instruction == ficlInstructionLoopParen)
index++;
else
{
ficlInteger increment;
CHECK_STACK(1, 0);
increment = (dataTop--)->i;
index += increment;
direction = (increment < 0);
}
if (direction ^ (index >= limit))
{
returnTop -= 3; /* nuke the loop indices & "leave" addr */
ip++; /* fall through the loop */
}
else
{ /* update index, branch to loop head */
returnTop->i = index;
BRANCH();
}
break;
}
/*
** Runtime code to break out of a do..loop construct
** Drop the loop control variables; the branch address
** past "loop" is next on the return stack.
*/
case ficlInstructionLeave:
{
/* almost unloop */
returnTop -= 2;
/* exit */
EXIT_FUNCTION();
}
case ficlInstructionUnloop:
{
returnTop -= 3;
break;
}
case ficlInstructionI:
{
*++dataTop = *returnTop;
break;
}
case ficlInstructionJ:
{
*++dataTop = returnTop[-3];
break;
}
case ficlInstructionK:
{
*++dataTop = returnTop[-6];
break;
}
case ficlInstructionDoesParen:
{
ficlDictionary *dictionary = ficlVmGetDictionary(vm);
dictionary->smudge->code = (ficlPrimitive)ficlInstructionDoDoes;
dictionary->smudge->param[0].p = ip;
ip = (ficlInstruction *)((returnTop--)->p);
break;
}
case ficlInstructionDoDoes:
{
ficlCell *cell;
ficlIp tempIP;
CHECK_STACK(0, 1);
cell = fw->param;
tempIP = (ficlIp)((*cell).p);
(++dataTop)->p = (cell + 1);
(++returnTop)->p = (void *)ip;
ip = (ficlInstruction *)tempIP;
break;
}
#if FICL_WANT_FLOAT
case ficlInstructionF2Fetch:
CHECK_FLOAT_STACK(0, 2);
CHECK_STACK(1, 0);
FLOAT_PUSH_CELL_POINTER_DOUBLE((ficlCell *)(dataTop--)->p);
case ficlInstructionFFetch:
CHECK_FLOAT_STACK(0, 1);
CHECK_STACK(1, 0);
FLOAT_PUSH_CELL_POINTER((ficlCell *)(dataTop--)->p);
case ficlInstructionF2Store:
CHECK_FLOAT_STACK(2, 0);
CHECK_STACK(1, 0);
FLOAT_POP_CELL_POINTER_DOUBLE((ficlCell *)(dataTop--)->p);
case ficlInstructionFStore:
CHECK_FLOAT_STACK(1, 0);
CHECK_STACK(1, 0);
FLOAT_POP_CELL_POINTER((ficlCell *)(dataTop--)->p);
#endif /* FICL_WANT_FLOAT */
/*
** two-fetch CORE ( a-addr -- x1 x2 )
**
** Fetch the ficlCell pair x1 x2 stored at a-addr. x2 is stored at a-addr
** and x1 at the next consecutive ficlCell. It is equivalent to the
** sequence DUP ficlCell+ @ SWAP @ .
*/
case ficlInstruction2Fetch:
CHECK_STACK(1, 2);
PUSH_CELL_POINTER_DOUBLE((ficlCell *)(dataTop--)->p);
/*
** fetch CORE ( a-addr -- x )
**
** x is the value stored at a-addr.
*/
case ficlInstructionFetch:
CHECK_STACK(1, 1);
PUSH_CELL_POINTER((ficlCell *)(dataTop--)->p);
/*
** two-store CORE ( x1 x2 a-addr -- )
** Store the ficlCell pair x1 x2 at a-addr, with x2 at a-addr and x1 at the
** next consecutive ficlCell. It is equivalent to the sequence
** SWAP OVER ! ficlCell+ ! .
*/
case ficlInstruction2Store:
CHECK_STACK(3, 0);
POP_CELL_POINTER_DOUBLE((ficlCell *)(dataTop--)->p);
/*
** store CORE ( x a-addr -- )
** Store x at a-addr.
*/
case ficlInstructionStore:
CHECK_STACK(2, 0);
POP_CELL_POINTER((ficlCell *)(dataTop--)->p);
case ficlInstructionComma:
{
ficlDictionary *dictionary;
CHECK_STACK(1, 0);
dictionary = ficlVmGetDictionary(vm);
ficlDictionaryAppendCell(dictionary, *dataTop--);
break;
}
case ficlInstructionCComma:
{
ficlDictionary *dictionary;
char c;
CHECK_STACK(1, 0);
dictionary = ficlVmGetDictionary(vm);
c = (char)(dataTop--)->i;
ficlDictionaryAppendCharacter(dictionary, c);
break;
}
case ficlInstructionCells:
{
CHECK_STACK(1, 1);
dataTop->i *= sizeof(ficlCell);
break;
}
case ficlInstructionCellPlus:
{
CHECK_STACK(1, 1);
dataTop->i += sizeof(ficlCell);
break;
}
case ficlInstructionStar:
{
CHECK_STACK(2, 1);
i = (dataTop--)->i;
dataTop->i *= i;
break;
}
case ficlInstructionNegate:
{
CHECK_STACK(1, 1);
dataTop->i = - dataTop->i;
break;
}
case ficlInstructionSlash:
{
CHECK_STACK(2, 1);
i = (dataTop--)->i;
dataTop->i /= i;
break;
}
/*
** slash-mod CORE ( n1 n2 -- n3 n4 )
** Divide n1 by n2, giving the single-ficlCell remainder n3 and the single-ficlCell
** quotient n4. An ambiguous condition exists if n2 is zero. If n1 and n2
** differ in sign, the implementation-defined result returned will be the
** same as that returned by either the phrase
** >R S>D R> FM/MOD or the phrase >R S>D R> SM/REM .
** NOTE: Ficl complies with the second phrase (symmetric division)
*/
case ficlInstructionSlashMod:
{
ficl2Integer n1;
ficlInteger n2;
ficl2IntegerQR qr;
CHECK_STACK(2, 2);
n2 = dataTop[0].i;
FICL_INTEGER_TO_2INTEGER(dataTop[-1].i, n1);
qr = ficl2IntegerDivideSymmetric(n1, n2);
dataTop[-1].i = qr.remainder;
dataTop[0].i = FICL_2UNSIGNED_GET_LOW(qr.quotient);
break;
}
case ficlInstruction2Star:
{
CHECK_STACK(1, 1);
dataTop->i <<= 1;
break;
}
case ficlInstruction2Slash:
{
CHECK_STACK(1, 1);
dataTop->i >>= 1;
break;
}
case ficlInstructionStarSlash:
{
ficlInteger x, y, z;
ficl2Integer prod;
CHECK_STACK(3, 1);
z = (dataTop--)->i;
y = (dataTop--)->i;
x = dataTop->i;
prod = ficl2IntegerMultiply(x,y);
dataTop->i = FICL_2UNSIGNED_GET_LOW(ficl2IntegerDivideSymmetric(prod, z).quotient);
break;
}
case ficlInstructionStarSlashMod:
{
ficlInteger x, y, z;
ficl2Integer prod;
ficl2IntegerQR qr;
CHECK_STACK(3, 2);
z = (dataTop--)->i;
y = dataTop[0].i;
x = dataTop[-1].i;
prod = ficl2IntegerMultiply(x,y);
qr = ficl2IntegerDivideSymmetric(prod, z);
dataTop[-1].i = qr.remainder;
dataTop[0].i = FICL_2UNSIGNED_GET_LOW(qr.quotient);
break;
}
#if FICL_WANT_FLOAT
case ficlInstructionF0:
{
CHECK_FLOAT_STACK(0, 1);
(++floatTop)->f = 0.0f;
break;
}
case ficlInstructionF1:
{
CHECK_FLOAT_STACK(0, 1);
(++floatTop)->f = 1.0f;
break;
}
case ficlInstructionFNeg1:
{
CHECK_FLOAT_STACK(0, 1);
(++floatTop)->f = -1.0f;
break;
}
/*******************************************************************
** Floating point literal execution word.
*******************************************************************/
case ficlInstructionFLiteralParen:
{
CHECK_FLOAT_STACK(0, 1);
/* Yes, I'm using ->i here, but it's really a float. --lch */
(++floatTop)->i = *ip++;
break;
}
/*******************************************************************
** Do float addition r1 + r2.
** f+ ( r1 r2 -- r )
*******************************************************************/
case ficlInstructionFPlus:
{
ficlFloat f;
CHECK_FLOAT_STACK(2, 1);
f = (floatTop--)->f;
floatTop->f += f;
break;
}
/*******************************************************************
** Do float subtraction r1 - r2.
** f- ( r1 r2 -- r )
*******************************************************************/
case ficlInstructionFMinus:
{
ficlFloat f;
CHECK_FLOAT_STACK(2, 1);
f = (floatTop--)->f;
floatTop->f -= f;
break;
}
/*******************************************************************
** Do float multiplication r1 * r2.
** f* ( r1 r2 -- r )
*******************************************************************/
case ficlInstructionFStar:
{
ficlFloat f;
CHECK_FLOAT_STACK(2, 1);
f = (floatTop--)->f;
floatTop->f *= f;
break;
}
/*******************************************************************
** Do float negation.
** fnegate ( r -- r )
*******************************************************************/
case ficlInstructionFNegate:
{
CHECK_FLOAT_STACK(1, 1);
floatTop->f = -(floatTop->f);
break;
}
/*******************************************************************
** Do float division r1 / r2.
** f/ ( r1 r2 -- r )
*******************************************************************/
case ficlInstructionFSlash:
{
ficlFloat f;
CHECK_FLOAT_STACK(2, 1);
f = (floatTop--)->f;
floatTop->f /= f;
break;
}
/*******************************************************************
** Do float + integer r + n.
** f+i ( r n -- r )
*******************************************************************/
case ficlInstructionFPlusI:
{
ficlFloat f;
CHECK_FLOAT_STACK(1, 1);
CHECK_STACK(1, 0);
f = (ficlFloat)(dataTop--)->f;
floatTop->f += f;
break;
}
/*******************************************************************
** Do float - integer r - n.
** f-i ( r n -- r )
*******************************************************************/
case ficlInstructionFMinusI:
{
ficlFloat f;
CHECK_FLOAT_STACK(1, 1);
CHECK_STACK(1, 0);
f = (ficlFloat)(dataTop--)->f;
floatTop->f -= f;
break;
}
/*******************************************************************
** Do float * integer r * n.
** f*i ( r n -- r )
*******************************************************************/
case ficlInstructionFStarI:
{
ficlFloat f;
CHECK_FLOAT_STACK(1, 1);
CHECK_STACK(1, 0);
f = (ficlFloat)(dataTop--)->f;
floatTop->f *= f;
break;
}
/*******************************************************************
** Do float / integer r / n.
** f/i ( r n -- r )
*******************************************************************/
case ficlInstructionFSlashI:
{
ficlFloat f;
CHECK_FLOAT_STACK(1, 1);
CHECK_STACK(1, 0);
f = (ficlFloat)(dataTop--)->f;
floatTop->f /= f;
break;
}
/*******************************************************************
** Do integer - float n - r.
** i-f ( n r -- r )
*******************************************************************/
case ficlInstructionIMinusF:
{
ficlFloat f;
CHECK_FLOAT_STACK(1, 1);
CHECK_STACK(1, 0);
f = (ficlFloat)(dataTop--)->f;
floatTop->f = f - floatTop->f;
break;
}
/*******************************************************************
** Do integer / float n / r.
** i/f ( n r -- r )
*******************************************************************/
case ficlInstructionISlashF:
{
ficlFloat f;
CHECK_FLOAT_STACK(1,1);
CHECK_STACK(1, 0);
f = (ficlFloat)(dataTop--)->f;
floatTop->f = f / floatTop->f;
break;
}
/*******************************************************************
** Do integer to float conversion.
** int>float ( n -- r )
*******************************************************************/
case ficlInstructionIntToFloat:
{
CHECK_STACK(1, 0);
CHECK_FLOAT_STACK(0, 1);
(++floatTop)->f = (ficlFloat)((dataTop--)->i);
break;
}
/*******************************************************************
** Do float to integer conversion.
** float>int ( r -- n )
*******************************************************************/
case ficlInstructionFloatToInt:
{
CHECK_STACK(0, 1);
CHECK_FLOAT_STACK(1, 0);
(++dataTop)->i = (ficlInteger)((floatTop--)->f);
break;
}
/*******************************************************************
** Add a floating point number to contents of a variable.
** f+! ( r n -- )
*******************************************************************/
case ficlInstructionFPlusStore:
{
ficlCell *cell;
CHECK_STACK(1, 0);
CHECK_FLOAT_STACK(1, 0);
cell = (ficlCell *)(dataTop--)->p;
cell->f += (floatTop--)->f;
break;
}
/*******************************************************************
** Do float stack drop.
** fdrop ( r -- )
*******************************************************************/
case ficlInstructionFDrop:
{
CHECK_FLOAT_STACK(1, 0);
floatTop--;
break;
}
/*******************************************************************
** Do float stack ?dup.
** f?dup ( r -- r )
*******************************************************************/
case ficlInstructionFQuestionDup:
{
CHECK_FLOAT_STACK(1, 2);
if (floatTop->f != 0)
goto FDUP;
break;
}
/*******************************************************************
** Do float stack dup.
** fdup ( r -- r r )
*******************************************************************/
case ficlInstructionFDup:
{
CHECK_FLOAT_STACK(1, 2);
FDUP:
floatTop[1] = floatTop[0];
floatTop++;
break;
}
/*******************************************************************
** Do float stack swap.
** fswap ( r1 r2 -- r2 r1 )
*******************************************************************/
case ficlInstructionFSwap:
{
CHECK_FLOAT_STACK(2, 2);
c = floatTop[0];
floatTop[0] = floatTop[-1];
floatTop[-1] = c;
break;
}
/*******************************************************************
** Do float stack 2drop.
** f2drop ( r r -- )
*******************************************************************/
case ficlInstructionF2Drop:
{
CHECK_FLOAT_STACK(2, 0);
floatTop -= 2;
break;
}
/*******************************************************************
** Do float stack 2dup.
** f2dup ( r1 r2 -- r1 r2 r1 r2 )
*******************************************************************/
case ficlInstructionF2Dup:
{
CHECK_FLOAT_STACK(2, 4);
floatTop[1] = floatTop[-1];
floatTop[2] = *floatTop;
floatTop += 2;
break;
}
/*******************************************************************
** Do float stack over.
** fover ( r1 r2 -- r1 r2 r1 )
*******************************************************************/
case ficlInstructionFOver:
{
CHECK_FLOAT_STACK(2, 3);
floatTop[1] = floatTop[-1];
floatTop++;
break;
}
/*******************************************************************
** Do float stack 2over.
** f2over ( r1 r2 r3 -- r1 r2 r3 r1 r2 )
*******************************************************************/
case ficlInstructionF2Over:
{
CHECK_FLOAT_STACK(4, 6);
floatTop[1] = floatTop[-2];
floatTop[2] = floatTop[-1];
floatTop += 2;
break;
}
/*******************************************************************
** Do float stack pick.
** fpick ( n -- r )
*******************************************************************/
case ficlInstructionFPick:
{
CHECK_STACK(1, 0);
c = *dataTop--;
CHECK_FLOAT_STACK(c.i+1, c.i+2);
floatTop[1] = floatTop[- c.i];
break;
}
/*******************************************************************
** Do float stack rot.
** frot ( r1 r2 r3 -- r2 r3 r1 )
*******************************************************************/
case ficlInstructionFRot:
{
i = 2;
goto FROLL;
}
/*******************************************************************
** Do float stack roll.
** froll ( n -- )
*******************************************************************/
case ficlInstructionFRoll:
{
CHECK_STACK(1, 0);
i = (dataTop--)->i;
if (i < 1)
break;
FROLL:
CHECK_FLOAT_STACK(i+1, i+2);
c = floatTop[-i];
memmove(floatTop - i, floatTop - (i - 1), i * sizeof(ficlCell));
*floatTop = c;
break;
}
/*******************************************************************
** Do float stack -rot.
** f-rot ( r1 r2 r3 -- r3 r1 r2 )
*******************************************************************/
case ficlInstructionFMinusRot:
{
i = 2;
goto FMINUSROLL;
}
/*******************************************************************
** Do float stack -roll.
** f-roll ( n -- )
*******************************************************************/
case ficlInstructionFMinusRoll:
{
CHECK_STACK(1, 0);
i = (dataTop--)->i;
if (i < 1)
break;
FMINUSROLL:
CHECK_FLOAT_STACK(i+1, i+2);
c = *floatTop;
memmove(floatTop - (i - 1), floatTop - i, i * sizeof(ficlCell));
floatTop[-i] = c;
break;
}
/*******************************************************************
** Do float stack 2swap
** f2swap ( r1 r2 r3 r4 -- r3 r4 r1 r2 )
*******************************************************************/
case ficlInstructionF2Swap:
{
ficlCell c2;
CHECK_FLOAT_STACK(4, 4);
c = *floatTop;
c2 = floatTop[-1];
*floatTop = floatTop[-2];
floatTop[-1] = floatTop[-3];
floatTop[-2] = c;
floatTop[-3] = c2;
break;
}
/*******************************************************************
** Do float 0= comparison r = 0.0.
** f0= ( r -- T/F )
*******************************************************************/
case ficlInstructionF0Equals:
{
CHECK_FLOAT_STACK(1, 0);
CHECK_STACK(0, 1);
(++dataTop)->i = FICL_BOOL((floatTop--)->f != 0.0f);
break;
}
/*******************************************************************
** Do float 0< comparison r < 0.0.
** f0< ( r -- T/F )
*******************************************************************/
case ficlInstructionF0Less:
{
CHECK_FLOAT_STACK(1, 0);
CHECK_STACK(0, 1);
(++dataTop)->i = FICL_BOOL((floatTop--)->f < 0.0f);
break;
}
/*******************************************************************
** Do float 0> comparison r > 0.0.
** f0> ( r -- T/F )
*******************************************************************/
case ficlInstructionF0Greater:
{
CHECK_FLOAT_STACK(1, 0);
CHECK_STACK(0, 1);
(++dataTop)->i = FICL_BOOL((floatTop--)->f > 0.0f);
break;
}
/*******************************************************************
** Do float = comparison r1 = r2.
** f= ( r1 r2 -- T/F )
*******************************************************************/
case ficlInstructionFEquals:
{
ficlFloat f;
CHECK_FLOAT_STACK(2, 0);
CHECK_STACK(0, 1);
f = (floatTop--)->f;
(++dataTop)->i = FICL_BOOL((floatTop--)->f == f);
break;
}
/*******************************************************************
** Do float < comparison r1 < r2.
** f< ( r1 r2 -- T/F )
*******************************************************************/
case ficlInstructionFLess:
{
ficlFloat f;
CHECK_FLOAT_STACK(2, 0);
CHECK_STACK(0, 1);
f = (floatTop--)->f;
(++dataTop)->i = FICL_BOOL((floatTop--)->f < f);
break;
}
/*******************************************************************
** Do float > comparison r1 > r2.
** f> ( r1 r2 -- T/F )
*******************************************************************/
case ficlInstructionFGreater:
{
ficlFloat f;
CHECK_FLOAT_STACK(2, 0);
CHECK_STACK(0, 1);
f = (floatTop--)->f;
(++dataTop)->i = FICL_BOOL((floatTop--)->f > f);
break;
}
/*******************************************************************
** Move float to param stack (assumes they both fit in a single ficlCell)
** f>s
*******************************************************************/
case ficlInstructionFFrom:
{
CHECK_FLOAT_STACK(1, 0);
CHECK_STACK(0, 1);
*++dataTop = *floatTop--;
break;
}
case ficlInstructionToF:
{
CHECK_FLOAT_STACK(0, 1);
CHECK_STACK(1, 0);
*++floatTop = *dataTop--;
break;
}
#endif /* FICL_WANT_FLOAT */
/**************************************************************************
c o l o n P a r e n
** This is the code that executes a colon definition. It assumes that the
** virtual machine is running a "next" loop (See the vm.c
** for its implementation of member function vmExecute()). The colon
** code simply copies the address of the first word in the list of words
** to interpret into IP after saving its old value. When we return to the
** "next" loop, the virtual machine will call the code for each word in
** turn.
**
**************************************************************************/
case ficlInstructionColonParen:
{
(++returnTop)->p = (void *)ip;
ip = (ficlInstruction *)(fw->param);
break;
}
case ficlInstructionCreateParen:
{
CHECK_STACK(0, 1);
(++dataTop)->p = (fw->param + 1);
break;
}
case ficlInstructionVariableParen:
{
CHECK_STACK(0, 1);
(++dataTop)->p = fw->param;
break;
}
/**************************************************************************
c o n s t a n t P a r e n
** This is the run-time code for "constant". It simply returns the
** contents of its word's first data ficlCell.
**
**************************************************************************/
#if FICL_WANT_FLOAT
case ficlInstructionF2ConstantParen:
CHECK_FLOAT_STACK(0, 2);
FLOAT_PUSH_CELL_POINTER_DOUBLE(fw->param);
case ficlInstructionFConstantParen:
CHECK_FLOAT_STACK(0, 1);
FLOAT_PUSH_CELL_POINTER(fw->param);
#endif /* FICL_WANT_FLOAT */
case ficlInstruction2ConstantParen:
CHECK_STACK(0, 2);
PUSH_CELL_POINTER_DOUBLE(fw->param);
case ficlInstructionConstantParen:
CHECK_STACK(0, 1);
PUSH_CELL_POINTER(fw->param);
#if FICL_WANT_USER
case ficlInstructionUserParen:
{
ficlInteger i = fw->param[0].i;
(++dataTop)->p = &vm->user[i];
break;
}
#endif
default:
{
/*
** Clever hack, or evil coding? You be the judge.
**
** If the word we've been asked to execute is in fact
** an *instruction*, we grab the instruction, stow it
** in "i" (our local cache of *ip), and *jump* to the
** top of the switch statement. --lch
*/
if ((ficlInstruction)fw->code < ficlInstructionLast)
{
instruction = (ficlInstruction)fw->code;
goto AGAIN;
}
LOCAL_VARIABLE_SPILL;
(vm)->runningWord = fw;
fw->code(vm);
LOCAL_VARIABLE_REFILL;
break;
}
}
}
LOCAL_VARIABLE_SPILL;
vm->exceptionHandler = oldExceptionHandler;
}
/**************************************************************************
v m G e t D i c t
** Returns the address dictionary for this VM's system
**************************************************************************/
ficlDictionary *ficlVmGetDictionary(ficlVm *vm)
{
FICL_VM_ASSERT(vm, vm);
return vm->callback.system->dictionary;
}
/**************************************************************************
v m G e t S t r i n g
** Parses a string out of the VM input buffer and copies up to the first
** FICL_COUNTED_STRING_MAX characters to the supplied destination buffer, a
** ficlCountedString. The destination string is NULL terminated.
**
** Returns the address of the first unused character in the dest buffer.
**************************************************************************/
char *ficlVmGetString(ficlVm *vm, ficlCountedString *counted, char delimiter)
{
ficlString s = ficlVmParseStringEx(vm, delimiter, 0);
if (FICL_STRING_GET_LENGTH(s) > FICL_COUNTED_STRING_MAX)
{
FICL_STRING_SET_LENGTH(s, FICL_COUNTED_STRING_MAX);
}
strncpy(counted->text, FICL_STRING_GET_POINTER(s), FICL_STRING_GET_LENGTH(s));
counted->text[FICL_STRING_GET_LENGTH(s)] = '\0';
counted->length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(s);
return counted->text + FICL_STRING_GET_LENGTH(s) + 1;
}
/**************************************************************************
v m G e t W o r d
** vmGetWord calls vmGetWord0 repeatedly until it gets a string with
** non-zero length.
**************************************************************************/
ficlString ficlVmGetWord(ficlVm *vm)
{
ficlString s = ficlVmGetWord0(vm);
if (FICL_STRING_GET_LENGTH(s) == 0)
{
ficlVmThrow(vm, FICL_VM_STATUS_RESTART);
}
return s;
}
/**************************************************************************
v m G e t W o r d 0
** Skip leading whitespace and parse a space delimited word from the tib.
** Returns the start address and length of the word. Updates the tib
** to reflect characters consumed, including the trailing delimiter.
** If there's nothing of interest in the tib, returns zero. This function
** does not use vmParseString because it uses isspace() rather than a
** single delimiter character.
**************************************************************************/
ficlString ficlVmGetWord0(ficlVm *vm)
{
char *trace = ficlVmGetInBuf(vm);
char *stop = ficlVmGetInBufEnd(vm);
ficlString s;
ficlUnsigned length = 0;
char c = 0;
trace = ficlStringSkipSpace(trace, stop);
FICL_STRING_SET_POINTER(s, trace);
/* Please leave this loop this way; it makes Purify happier. --lch */
for (;;)
{
if (trace == stop)
break;
c = *trace;
if (isspace((unsigned char)c))
break;
length++;
trace++;
}
FICL_STRING_SET_LENGTH(s, length);
if ((trace != stop) && isspace((unsigned char)c)) /* skip one trailing delimiter */
trace++;
ficlVmUpdateTib(vm, trace);
return s;
}
/**************************************************************************
v m G e t W o r d T o P a d
** Does vmGetWord and copies the result to the pad as a NULL terminated
** string. Returns the length of the string. If the string is too long
** to fit in the pad, it is truncated.
**************************************************************************/
int ficlVmGetWordToPad(ficlVm *vm)
{
ficlString s;
char *pad = (char *)vm->pad;
s = ficlVmGetWord(vm);
if (FICL_STRING_GET_LENGTH(s) > FICL_PAD_SIZE)
FICL_STRING_SET_LENGTH(s, FICL_PAD_SIZE);
strncpy(pad, FICL_STRING_GET_POINTER(s), FICL_STRING_GET_LENGTH(s));
pad[FICL_STRING_GET_LENGTH(s)] = '\0';
return (int)(FICL_STRING_GET_LENGTH(s));
}
/**************************************************************************
v m P a r s e S t r i n g
** Parses a string out of the input buffer using the delimiter
** specified. Skips leading delimiters, marks the start of the string,
** and counts characters to the next delimiter it encounters. It then
** updates the vm input buffer to consume all these chars, including the
** trailing delimiter.
** Returns the address and length of the parsed string, not including the
** trailing delimiter.
**************************************************************************/
ficlString ficlVmParseString(ficlVm *vm, char delimiter)
{
return ficlVmParseStringEx(vm, delimiter, 1);
}
ficlString ficlVmParseStringEx(ficlVm *vm, char delimiter, char skipLeadingDelimiters)
{
ficlString s;
char *trace = ficlVmGetInBuf(vm);
char *stop = ficlVmGetInBufEnd(vm);
char c;
if (skipLeadingDelimiters)
{
while ((trace != stop) && (*trace == delimiter))
trace++;
}
FICL_STRING_SET_POINTER(s, trace); /* mark start of text */
for (c = *trace;
(trace != stop) && (c != delimiter)
&& (c != '\r') && (c != '\n');
c = *++trace)
{
; /* find next delimiter or end of line */
}
/* set length of result */
FICL_STRING_SET_LENGTH(s, trace - FICL_STRING_GET_POINTER(s));
if ((trace != stop) && (*trace == delimiter)) /* gobble trailing delimiter */
trace++;
ficlVmUpdateTib(vm, trace);
return s;
}
/**************************************************************************
v m P o p
**
**************************************************************************/
ficlCell ficlVmPop(ficlVm *vm)
{
return ficlStackPop(vm->dataStack);
}
/**************************************************************************
v m P u s h
**
**************************************************************************/
void ficlVmPush(ficlVm *vm, ficlCell c)
{
ficlStackPush(vm->dataStack, c);
return;
}
/**************************************************************************
v m P o p I P
**
**************************************************************************/
void ficlVmPopIP(ficlVm *vm)
{
vm->ip = (ficlIp)(ficlStackPopPointer(vm->returnStack));
return;
}
/**************************************************************************
v m P u s h I P
**
**************************************************************************/
void ficlVmPushIP(ficlVm *vm, ficlIp newIP)
{
ficlStackPushPointer(vm->returnStack, (void *)vm->ip);
vm->ip = newIP;
return;
}
/**************************************************************************
v m P u s h T i b
** Binds the specified input string to the VM and clears >IN (the index)
**************************************************************************/
void ficlVmPushTib(ficlVm *vm, char *text, ficlInteger nChars, ficlTIB *pSaveTib)
{
if (pSaveTib)
{
*pSaveTib = vm->tib;
}
vm->tib.text = text;
vm->tib.end = text + nChars;
vm->tib.index = 0;
}
void ficlVmPopTib(ficlVm *vm, ficlTIB *pTib)
{
if (pTib)
{
vm->tib = *pTib;
}
return;
}
/**************************************************************************
v m Q u i t
**
**************************************************************************/
void ficlVmQuit(ficlVm *vm)
{
ficlStackReset(vm->returnStack);
vm->restart = 0;
vm->ip = NULL;
vm->runningWord = NULL;
vm->state = FICL_VM_STATE_INTERPRET;
vm->tib.text = NULL;
vm->tib.end = NULL;
vm->tib.index = 0;
vm->pad[0] = '\0';
vm->sourceId.i = 0;
return;
}
/**************************************************************************
v m R e s e t
**
**************************************************************************/
void ficlVmReset(ficlVm *vm)
{
ficlVmQuit(vm);
ficlStackReset(vm->dataStack);
#if FICL_WANT_FLOAT
ficlStackReset(vm->floatStack);
#endif
vm->base = 10;
return;
}
/**************************************************************************
v m S e t T e x t O u t
** Binds the specified output callback to the vm. If you pass NULL,
** binds the default output function (ficlTextOut)
**************************************************************************/
void ficlVmSetTextOut(ficlVm *vm, ficlOutputFunction textOut)
{
vm->callback.textOut = textOut;
return;
}
void ficlVmTextOut(ficlVm *vm, char *text)
{
ficlCallbackTextOut((ficlCallback *)vm, text);
}
void ficlVmErrorOut(ficlVm *vm, char *text)
{
ficlCallbackErrorOut((ficlCallback *)vm, text);
}
/**************************************************************************
v m T h r o w
**
**************************************************************************/
void ficlVmThrow(ficlVm *vm, int except)
{
if (vm->exceptionHandler)
longjmp(*(vm->exceptionHandler), except);
}
void ficlVmThrowError(ficlVm *vm, char *fmt, ...)
{
va_list list;
va_start(list, fmt);
vsprintf(vm->pad, fmt, list);
va_end(list);
strcat(vm->pad, "\n");
longjmp(*(vm->exceptionHandler), FICL_VM_STATUS_ERROR_EXIT);
}
void ficlVmThrowErrorVararg(ficlVm *vm, char *fmt, va_list list)
{
vsprintf(vm->pad, fmt, list);
/* well, we can try anyway, we're certainly not returning to our caller! */
va_end(list);
strcat(vm->pad, "\n");
longjmp(*(vm->exceptionHandler), FICL_VM_STATUS_ERROR_EXIT);
}
/**************************************************************************
f i c l E v a l u a t e
** Wrapper for ficlExec() which sets SOURCE-ID to -1.
**************************************************************************/
int ficlVmEvaluate(ficlVm *vm, char *s)
{
int returnValue;
ficlCell id = vm->sourceId;
ficlString string;
vm->sourceId.i = -1;
FICL_STRING_SET_FROM_CSTRING(string, s);
returnValue = ficlVmExecuteString(vm, string);
vm->sourceId = id;
return returnValue;
}
/**************************************************************************
f i c l E x e c
** Evaluates a block of input text in the context of the
** specified interpreter. Emits any requested output to the
** interpreter's output function.
**
** Contains the "inner interpreter" code in a tight loop
**
** Returns one of the VM_XXXX codes defined in ficl.h:
** VM_OUTOFTEXT is the normal exit condition
** VM_ERREXIT means that the interpreter encountered a syntax error
** and the vm has been reset to recover (some or all
** of the text block got ignored
** VM_USEREXIT means that the user executed the "bye" command
** to shut down the interpreter. This would be a good
** time to delete the vm, etc -- or you can ignore this
** signal.
**************************************************************************/
int ficlVmExecuteString(ficlVm *vm, ficlString s)
{
ficlSystem *system = vm->callback.system;
ficlDictionary *dictionary = system->dictionary;
int except;
jmp_buf vmState;
jmp_buf *oldState;
ficlTIB saveficlTIB;
FICL_VM_ASSERT(vm, vm);
FICL_VM_ASSERT(vm, system->interpreterLoop[0]);
ficlVmPushTib(vm, FICL_STRING_GET_POINTER(s), FICL_STRING_GET_LENGTH(s), &saveficlTIB);
/*
** Save and restore VM's jmp_buf to enable nested calls to ficlExec
*/
oldState = vm->exceptionHandler;
vm->exceptionHandler = &vmState; /* This has to come before the setjmp! */
except = setjmp(vmState);
switch (except)
{
case 0:
if (vm->restart)
{
vm->runningWord->code(vm);
vm->restart = 0;
}
else
{ /* set VM up to interpret text */
ficlVmPushIP(vm, (ficlWord**)&(system->interpreterLoop[0]));
}
ficlVmInnerLoop(vm, 0);
break;
case FICL_VM_STATUS_RESTART:
vm->restart = 1;
except = FICL_VM_STATUS_OUT_OF_TEXT;
break;
case FICL_VM_STATUS_OUT_OF_TEXT:
ficlVmPopIP(vm);
if ((vm->state != FICL_VM_STATE_COMPILE) && (vm->sourceId.i == 0))
ficlVmTextOut(vm, FICL_PROMPT);
break;
case FICL_VM_STATUS_USER_EXIT:
case FICL_VM_STATUS_INNER_EXIT:
case FICL_VM_STATUS_BREAK:
break;
case FICL_VM_STATUS_QUIT:
if (vm->state == FICL_VM_STATE_COMPILE)
{
ficlDictionaryAbortDefinition(dictionary);
#if FICL_WANT_LOCALS
ficlDictionaryEmpty(system->locals, system->locals->forthWordlist->size);
#endif
}
ficlVmQuit(vm);
break;
default: /* unhandled exception */
case FICL_VM_STATUS_ERROR_EXIT:
ficlVmErrorOut(vm, vm->pad); /* print saved message */
case FICL_VM_STATUS_ABORT:
case FICL_VM_STATUS_ABORTQ:
if (vm->state == FICL_VM_STATE_COMPILE)
{
ficlDictionaryAbortDefinition(dictionary);
#if FICL_WANT_LOCALS
ficlDictionaryEmpty(system->locals, system->locals->forthWordlist->size);
#endif
}
ficlDictionaryResetSearchOrder(dictionary);
ficlVmReset(vm);
break;
}
vm->exceptionHandler = oldState;
ficlVmPopTib(vm, &saveficlTIB);
return (except);
}
/**************************************************************************
f i c l E x e c X T
** Given a pointer to a ficlWord, push an inner interpreter and
** execute the word to completion. This is in contrast with vmExecute,
** which does not guarantee that the word will have completed when
** the function returns (ie in the case of colon definitions, which
** need an inner interpreter to finish)
**
** Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
** exit condition is VM_INNEREXIT, Ficl's private signal to exit the
** inner loop under normal circumstances. If another code is thrown to
** exit the loop, this function will re-throw it if it's nested under
** itself or ficlExec.
**
** NOTE: this function is intended so that C code can execute ficlWords
** given their address in the dictionary (xt).
**************************************************************************/
int ficlVmExecuteXT(ficlVm *vm, ficlWord *pWord)
{
int except;
jmp_buf vmState;
jmp_buf *oldState;
ficlWord *oldRunningWord;
FICL_VM_ASSERT(vm, vm);
FICL_VM_ASSERT(vm, vm->callback.system->exitInnerWord);
/*
** Save the runningword so that RESTART behaves correctly
** over nested calls.
*/
oldRunningWord = vm->runningWord;
/*
** Save and restore VM's jmp_buf to enable nested calls
*/
oldState = vm->exceptionHandler;
vm->exceptionHandler = &vmState; /* This has to come before the setjmp! */
except = setjmp(vmState);
if (except)
ficlVmPopIP(vm);
else
ficlVmPushIP(vm, &(vm->callback.system->exitInnerWord));
switch (except)
{
case 0:
ficlVmExecuteWord(vm, pWord);
ficlVmInnerLoop(vm, 0);
break;
case FICL_VM_STATUS_INNER_EXIT:
case FICL_VM_STATUS_BREAK:
break;
case FICL_VM_STATUS_RESTART:
case FICL_VM_STATUS_OUT_OF_TEXT:
case FICL_VM_STATUS_USER_EXIT:
case FICL_VM_STATUS_QUIT:
case FICL_VM_STATUS_ERROR_EXIT:
case FICL_VM_STATUS_ABORT:
case FICL_VM_STATUS_ABORTQ:
default: /* user defined exit code?? */
if (oldState)
{
vm->exceptionHandler = oldState;
ficlVmThrow(vm, except);
}
break;
}
vm->exceptionHandler = oldState;
vm->runningWord = oldRunningWord;
return (except);
}
/**************************************************************************
f i c l P a r s e N u m b e r
** Attempts to convert the NULL terminated string in the VM's pad to
** a number using the VM's current base. If successful, pushes the number
** onto the param stack and returns FICL_TRUE. Otherwise, returns FICL_FALSE.
** (jws 8/01) Trailing decimal point causes a zero ficlCell to be pushed. (See
** the standard for DOUBLE wordset.
**************************************************************************/
int ficlVmParseNumber(ficlVm *vm, ficlString s)
{
ficlInteger accumulator = 0;
char isNegative = 0;
char isDouble = 0;
unsigned base = vm->base;
char *trace = FICL_STRING_GET_POINTER(s);
ficlUnsigned8 length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(s);
unsigned c;
unsigned digit;
if (length > 1)
{
switch (*trace)
{
case '-':
trace++;
length--;
isNegative = 1;
break;
case '+':
trace++;
length--;
isNegative = 0;
break;
default:
break;
}
}
if ((length > 0) && (trace[length - 1] == '.')) /* detect & remove trailing decimal */
{
isDouble = 1;
length--;
}
if (length == 0) /* detect "+", "-", ".", "+." etc */
return 0; /* false */
while ((length--) && ((c = *trace++) != '\0'))
{
if (!isalnum(c))
return 0; /* false */
digit = c - '0';
if (digit > 9)
digit = tolower(c) - 'a' + 10;
if (digit >= base)
return 0; /* false */
accumulator = accumulator * base + digit;
}
if (isDouble) /* simple (required) DOUBLE support */
ficlStackPushInteger(vm->dataStack, 0);
if (isNegative)
accumulator = -accumulator;
ficlStackPushInteger(vm->dataStack, accumulator);
if (vm->state == FICL_VM_STATE_COMPILE)
ficlPrimitiveLiteralIm(vm);
return 1; /* true */
}
/**************************************************************************
d i c t C h e c k
** Checks the dictionary for corruption and throws appropriate
** errors.
** Input: +n number of ADDRESS UNITS (not ficlCells) proposed to allot
** -n number of ADDRESS UNITS proposed to de-allot
** 0 just do a consistency check
**************************************************************************/
void ficlVmDictionarySimpleCheck(ficlVm *vm, ficlDictionary *dictionary, int cells)
#if FICL_ROBUST >= 1
{
if ((cells >= 0) && (ficlDictionaryCellsAvailable(dictionary) * (int)sizeof(ficlCell) < cells))
{
ficlVmThrowError(vm, "Error: dictionary full");
}
if ((cells <= 0) && (ficlDictionaryCellsUsed(dictionary) * (int)sizeof(ficlCell) < -cells))
{
ficlVmThrowError(vm, "Error: dictionary underflow");
}
return;
}
#else /* FICL_ROBUST >= 1 */
{
FICL_IGNORE(vm);
FICL_IGNORE(dictionary);
FICL_IGNORE(cells);
}
#endif /* FICL_ROBUST >= 1 */
void ficlVmDictionaryCheck(ficlVm *vm, ficlDictionary *dictionary, int cells)
#if FICL_ROBUST >= 1
{
ficlVmDictionarySimpleCheck(vm, dictionary, cells);
if (dictionary->wordlistCount > FICL_MAX_WORDLISTS)
{
ficlDictionaryResetSearchOrder(dictionary);
ficlVmThrowError(vm, "Error: search order overflow");
}
else if (dictionary->wordlistCount < 0)
{
ficlDictionaryResetSearchOrder(dictionary);
ficlVmThrowError(vm, "Error: search order underflow");
}
return;
}
#else /* FICL_ROBUST >= 1 */
{
FICL_IGNORE(vm);
FICL_IGNORE(dictionary);
FICL_IGNORE(cells);
}
#endif /* FICL_ROBUST >= 1 */
void ficlVmDictionaryAllot(ficlVm *vm, ficlDictionary *dictionary, int n)
{
FICL_VM_DICTIONARY_SIMPLE_CHECK(vm, dictionary, n);
FICL_IGNORE(vm);
ficlDictionaryAllot(dictionary, n);
}
void ficlVmDictionaryAllotCells(ficlVm *vm, ficlDictionary *dictionary, int cells)
{
FICL_VM_DICTIONARY_SIMPLE_CHECK(vm, dictionary, cells);
FICL_IGNORE(vm);
ficlDictionaryAllotCells(dictionary, cells);
}
/**************************************************************************
f i c l P a r s e W o r d
** From the standard, section 3.4
** b) Search the dictionary name space (see 3.4.2). If a definition name
** matching the string is found:
** 1.if interpreting, perform the interpretation semantics of the definition
** (see 3.4.3.2), and continue at a);
** 2.if compiling, perform the compilation semantics of the definition
** (see 3.4.3.3), and continue at a).
**
** c) If a definition name matching the string is not found, attempt to
** convert the string to a number (see 3.4.1.3). If successful:
** 1.if interpreting, place the number on the data stack, and continue at a);
** 2.if compiling, FICL_VM_STATE_COMPILE code that when executed will place the number on
** the stack (see 6.1.1780 LITERAL), and continue at a);
**
** d) If unsuccessful, an ambiguous condition exists (see 3.4.4).
**
** (jws 4/01) Modified to be a ficlParseStep
**************************************************************************/
int ficlVmParseWord(ficlVm *vm, ficlString name)
{
ficlDictionary *dictionary = ficlVmGetDictionary(vm);
ficlWord *tempFW;
FICL_VM_DICTIONARY_CHECK(vm, dictionary, 0);
FICL_STACK_CHECK(vm->dataStack, 0, 0);
#if FICL_WANT_LOCALS
if (vm->callback.system->localsCount > 0)
{
tempFW = ficlSystemLookupLocal(vm->callback.system, name);
}
else
#endif
tempFW = ficlDictionaryLookup(dictionary, name);
if (vm->state == FICL_VM_STATE_INTERPRET)
{
if (tempFW != NULL)
{
if (ficlWordIsCompileOnly(tempFW))
{
ficlVmThrowError(vm, "Error: FICL_VM_STATE_COMPILE only!");
}
ficlVmExecuteWord(vm, tempFW);
return 1; /* true */
}
}
else /* (vm->state == FICL_VM_STATE_COMPILE) */
{
if (tempFW != NULL)
{
if (ficlWordIsImmediate(tempFW))
{
ficlVmExecuteWord(vm, tempFW);
}
else
{
if (tempFW->flags & FICL_WORD_INSTRUCTION)
ficlDictionaryAppendUnsigned(dictionary, (ficlInteger)tempFW->code);
else
ficlDictionaryAppendCell(dictionary, FICL_LVALUE_TO_CELL(tempFW));
}
return 1; /* true */
}
}
return 0; /* false */
}