| /* |
| * w o r d s . c |
| * Forth Inspired Command Language |
| * ANS Forth CORE word-set written in C |
| * Author: John Sadler (john_sadler@alum.mit.edu) |
| * Created: 19 July 1997 |
| * $Id: primitives.c,v 1.4 2010/09/13 18:43:04 asau Exp $ |
| */ |
| /* |
| * 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 "ficl.h" |
| #include <limits.h> |
| |
| /* |
| * Control structure building words use these |
| * strings' addresses as markers on the stack to |
| * check for structure completion. |
| */ |
| static char doTag[] = "do"; |
| static char colonTag[] = "colon"; |
| static char leaveTag[] = "leave"; |
| |
| static char destTag[] = "target"; |
| static char origTag[] = "origin"; |
| |
| static char caseTag[] = "case"; |
| static char ofTag[] = "of"; |
| static char fallthroughTag[] = "fallthrough"; |
| |
| /* |
| * C O N T R O L S T R U C T U R E B U I L D E R S |
| * |
| * Push current dictionary location for later branch resolution. |
| * The location may be either a branch target or a patch address... |
| */ |
| static void |
| markBranch(ficlDictionary *dictionary, ficlVm *vm, char *tag) |
| { |
| ficlStackPushPointer(vm->dataStack, dictionary->here); |
| ficlStackPushPointer(vm->dataStack, tag); |
| } |
| |
| static void |
| markControlTag(ficlVm *vm, char *tag) |
| { |
| ficlStackPushPointer(vm->dataStack, tag); |
| } |
| |
| static void |
| matchControlTag(ficlVm *vm, char *wantTag) |
| { |
| char *tag; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| tag = (char *)ficlStackPopPointer(vm->dataStack); |
| |
| /* |
| * Changed the code below to compare the pointers first |
| * (by popular demand) |
| */ |
| if ((tag != wantTag) && strcmp(tag, wantTag)) { |
| ficlVmThrowError(vm, |
| "Error -- unmatched control structure \"%s\"", wantTag); |
| } |
| } |
| |
| /* |
| * Expect a branch target address on the param stack, |
| * FICL_VM_STATE_COMPILE a literal offset from the current dictionary location |
| * to the target address |
| */ |
| static void |
| resolveBackBranch(ficlDictionary *dictionary, ficlVm *vm, char *tag) |
| { |
| ficlCell *patchAddr, c; |
| |
| matchControlTag(vm, tag); |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack); |
| c.i = patchAddr - dictionary->here; |
| |
| ficlDictionaryAppendCell(dictionary, c); |
| } |
| |
| /* |
| * Expect a branch patch address on the param stack, |
| * FICL_VM_STATE_COMPILE a literal offset from the patch location |
| * to the current dictionary location |
| */ |
| static void |
| resolveForwardBranch(ficlDictionary *dictionary, ficlVm *vm, char *tag) |
| { |
| ficlInteger offset; |
| ficlCell *patchAddr; |
| |
| matchControlTag(vm, tag); |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack); |
| offset = dictionary->here - patchAddr; |
| (*patchAddr).i = offset; |
| } |
| |
| /* |
| * Match the tag to the top of the stack. If success, |
| * sopy "here" address into the ficlCell whose address is next |
| * on the stack. Used by do..leave..loop. |
| */ |
| static void |
| resolveAbsBranch(ficlDictionary *dictionary, ficlVm *vm, char *wantTag) |
| { |
| ficlCell *patchAddr; |
| char *tag; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 0); |
| |
| tag = ficlStackPopPointer(vm->dataStack); |
| |
| /* |
| * Changed the comparison below to compare the pointers first |
| * (by popular demand) |
| */ |
| if ((tag != wantTag) && strcmp(tag, wantTag)) { |
| ficlVmTextOut(vm, "Warning -- Unmatched control word: "); |
| ficlVmTextOut(vm, wantTag); |
| ficlVmTextOut(vm, "\n"); |
| } |
| |
| patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack); |
| (*patchAddr).p = dictionary->here; |
| } |
| |
| /* |
| * c o l o n d e f i n i t i o n s |
| * Code to begin compiling a colon definition |
| * This function sets the state to FICL_VM_STATE_COMPILE, then creates a |
| * new word whose name is the next word in the input stream |
| * and whose code is colonParen. |
| */ |
| static void |
| ficlPrimitiveColon(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlString name = ficlVmGetWord(vm); |
| |
| vm->state = FICL_VM_STATE_COMPILE; |
| markControlTag(vm, colonTag); |
| ficlDictionaryAppendWord(dictionary, name, |
| (ficlPrimitive)ficlInstructionColonParen, |
| FICL_WORD_DEFAULT | FICL_WORD_SMUDGED); |
| |
| #if FICL_WANT_LOCALS |
| vm->callback.system->localsCount = 0; |
| #endif |
| } |
| |
| static void |
| ficlPrimitiveSemicolonCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| matchControlTag(vm, colonTag); |
| |
| #if FICL_WANT_LOCALS |
| if (vm->callback.system->localsCount > 0) { |
| ficlDictionary *locals; |
| locals = ficlSystemGetLocals(vm->callback.system); |
| ficlDictionaryEmpty(locals, locals->forthWordlist->size); |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionUnlinkParen); |
| } |
| vm->callback.system->localsCount = 0; |
| #endif |
| |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstructionSemiParen); |
| vm->state = FICL_VM_STATE_INTERPRET; |
| ficlDictionaryUnsmudge(dictionary); |
| } |
| |
| /* |
| * e x i t |
| * CORE |
| * 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". |
| */ |
| static void |
| ficlPrimitiveExitCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| FICL_IGNORE(vm); |
| |
| #if FICL_WANT_LOCALS |
| if (vm->callback.system->localsCount > 0) { |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionUnlinkParen); |
| } |
| #endif |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstructionExitParen); |
| } |
| |
| /* |
| * c o n s t a n t |
| * IMMEDIATE |
| * Compiles a constant into the dictionary. Constants return their |
| * value when invoked. Expects a value on top of the parm stack. |
| */ |
| static void |
| ficlPrimitiveConstant(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlString name = ficlVmGetWord(vm); |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| ficlDictionaryAppendConstantInstruction(dictionary, name, |
| ficlInstructionConstantParen, ficlStackPopInteger(vm->dataStack)); |
| } |
| |
| static void |
| ficlPrimitive2Constant(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlString name = ficlVmGetWord(vm); |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 0); |
| |
| ficlDictionaryAppend2ConstantInstruction(dictionary, name, |
| ficlInstruction2ConstantParen, ficlStackPop2Integer(vm->dataStack)); |
| } |
| |
| /* |
| * d i s p l a y C e l l |
| * Drop and print the contents of the ficlCell at the top of the param |
| * stack |
| */ |
| static void |
| ficlPrimitiveDot(ficlVm *vm) |
| { |
| ficlCell c; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| c = ficlStackPop(vm->dataStack); |
| ficlLtoa((c).i, vm->pad, vm->base); |
| strcat(vm->pad, " "); |
| ficlVmTextOut(vm, vm->pad); |
| } |
| |
| static void |
| ficlPrimitiveUDot(ficlVm *vm) |
| { |
| ficlUnsigned u; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| u = ficlStackPopUnsigned(vm->dataStack); |
| ficlUltoa(u, vm->pad, vm->base); |
| strcat(vm->pad, " "); |
| ficlVmTextOut(vm, vm->pad); |
| } |
| |
| static void |
| ficlPrimitiveHexDot(ficlVm *vm) |
| { |
| ficlUnsigned u; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| u = ficlStackPopUnsigned(vm->dataStack); |
| ficlUltoa(u, vm->pad, 16); |
| strcat(vm->pad, " "); |
| ficlVmTextOut(vm, vm->pad); |
| } |
| |
| /* |
| * s t r l e n |
| * Ficl ( c-string -- length ) |
| * |
| * Returns the length of a C-style (zero-terminated) string. |
| * |
| * --lch |
| */ |
| static void |
| ficlPrimitiveStrlen(ficlVm *vm) |
| { |
| char *address = (char *)ficlStackPopPointer(vm->dataStack); |
| ficlStackPushInteger(vm->dataStack, strlen(address)); |
| } |
| |
| /* |
| * s p r i n t f |
| * Ficl ( i*x c-addr-fmt u-fmt c-addr-buffer u-buffer -- |
| * c-addr-buffer u-written success-flag ) |
| * Similar to the C sprintf() function. It formats into a buffer based on |
| * a "format" string. Each character in the format string is copied verbatim |
| * to the output buffer, until SPRINTF encounters a percent sign ("%"). |
| * SPRINTF then skips the percent sign, and examines the next character |
| * (the "format character"). Here are the valid format characters: |
| * s - read a C-ADDR U-LENGTH string from the stack and copy it to |
| * the buffer |
| * d - read a ficlCell from the stack, format it as a string (base-10, |
| * signed), and copy it to the buffer |
| * x - same as d, except in base-16 |
| * u - same as d, but unsigned |
| * % - output a literal percent-sign to the buffer |
| * SPRINTF returns the c-addr-buffer argument unchanged, the number of bytes |
| * written, and a flag indicating whether or not it ran out of space while |
| * writing to the output buffer (FICL_TRUE if it ran out of space). |
| * |
| * If SPRINTF runs out of space in the buffer to store the formatted string, |
| * it still continues parsing, in an effort to preserve your stack (otherwise |
| * it might leave uneaten arguments behind). |
| * |
| * --lch |
| */ |
| static void |
| ficlPrimitiveSprintf(ficlVm *vm) |
| { |
| int bufferLength = ficlStackPopInteger(vm->dataStack); |
| char *buffer = (char *)ficlStackPopPointer(vm->dataStack); |
| char *bufferStart = buffer; |
| |
| int formatLength = ficlStackPopInteger(vm->dataStack); |
| char *format = (char *)ficlStackPopPointer(vm->dataStack); |
| char *formatStop = format + formatLength; |
| |
| int base = 10; |
| int unsignedInteger = 0; /* false */ |
| |
| int append = 1; /* true */ |
| |
| while (format < formatStop) { |
| char scratch[64]; |
| char *source; |
| int actualLength; |
| int desiredLength; |
| int leadingZeroes; |
| |
| if (*format != '%') { |
| source = format; |
| actualLength = desiredLength = 1; |
| leadingZeroes = 0; |
| } else { |
| format++; |
| if (format == formatStop) |
| break; |
| |
| leadingZeroes = (*format == '0'); |
| if (leadingZeroes) { |
| format++; |
| if (format == formatStop) |
| break; |
| } |
| |
| desiredLength = isdigit((unsigned char)*format); |
| if (desiredLength) { |
| desiredLength = strtoul(format, &format, 10); |
| if (format == formatStop) |
| break; |
| } else if (*format == '*') { |
| desiredLength = |
| ficlStackPopInteger(vm->dataStack); |
| |
| format++; |
| if (format == formatStop) |
| break; |
| } |
| |
| switch (*format) { |
| case 's': |
| case 'S': |
| actualLength = |
| ficlStackPopInteger(vm->dataStack); |
| source = (char *) |
| ficlStackPopPointer(vm->dataStack); |
| break; |
| case 'x': |
| case 'X': |
| base = 16; |
| /* FALLTHROUGH */ |
| case 'u': |
| case 'U': |
| unsignedInteger = 1; /* true */ |
| /* FALLTHROUGH */ |
| case 'd': |
| case 'D': { |
| int integer; |
| integer = ficlStackPopInteger(vm->dataStack); |
| if (unsignedInteger) |
| ficlUltoa(integer, scratch, base); |
| else |
| ficlLtoa(integer, scratch, base); |
| base = 10; |
| unsignedInteger = 0; /* false */ |
| source = scratch; |
| actualLength = strlen(scratch); |
| break; |
| } |
| case '%': |
| source = format; |
| actualLength = 1; |
| /* FALLTHROUGH */ |
| default: |
| continue; |
| } |
| } |
| |
| if (append) { |
| if (!desiredLength) |
| desiredLength = actualLength; |
| if (desiredLength > bufferLength) { |
| append = 0; /* false */ |
| desiredLength = bufferLength; |
| } |
| while (desiredLength > actualLength) { |
| *buffer++ = (char)((leadingZeroes) ? '0' : ' '); |
| bufferLength--; |
| desiredLength--; |
| } |
| memcpy(buffer, source, actualLength); |
| buffer += actualLength; |
| bufferLength -= actualLength; |
| } |
| |
| format++; |
| } |
| |
| ficlStackPushPointer(vm->dataStack, bufferStart); |
| ficlStackPushInteger(vm->dataStack, buffer - bufferStart); |
| ficlStackPushInteger(vm->dataStack, FICL_BOOL(!append)); |
| } |
| |
| /* |
| * d u p & f r i e n d s |
| */ |
| static void |
| ficlPrimitiveDepth(ficlVm *vm) |
| { |
| int i; |
| |
| FICL_STACK_CHECK(vm->dataStack, 0, 1); |
| |
| i = ficlStackDepth(vm->dataStack); |
| ficlStackPushInteger(vm->dataStack, i); |
| } |
| |
| /* |
| * e m i t & f r i e n d s |
| */ |
| static void |
| ficlPrimitiveEmit(ficlVm *vm) |
| { |
| char buffer[2]; |
| int i; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| i = ficlStackPopInteger(vm->dataStack); |
| buffer[0] = (char)i; |
| buffer[1] = '\0'; |
| ficlVmTextOut(vm, buffer); |
| } |
| |
| static void |
| ficlPrimitiveCR(ficlVm *vm) |
| { |
| ficlVmTextOut(vm, "\n"); |
| } |
| |
| static void |
| ficlPrimitiveBackslash(ficlVm *vm) |
| { |
| char *trace = ficlVmGetInBuf(vm); |
| char *stop = ficlVmGetInBufEnd(vm); |
| char c = *trace; |
| |
| while ((trace != stop) && (c != '\r') && (c != '\n')) { |
| c = *++trace; |
| } |
| |
| /* |
| * Cope with DOS or UNIX-style EOLs - |
| * Check for /r, /n, /r/n, or /n/r end-of-line sequences, |
| * and point trace to next char. If EOL is \0, we're done. |
| */ |
| if (trace != stop) { |
| trace++; |
| |
| if ((trace != stop) && (c != *trace) && |
| ((*trace == '\r') || (*trace == '\n'))) |
| trace++; |
| } |
| |
| ficlVmUpdateTib(vm, trace); |
| } |
| |
| /* |
| * paren CORE |
| * Compilation: Perform the execution semantics given below. |
| * Execution: ( "ccc<paren>" -- ) |
| * Parse ccc delimited by ) (right parenthesis). ( is an immediate word. |
| * The number of characters in ccc may be zero to the number of characters |
| * in the parse area. |
| */ |
| static void |
| ficlPrimitiveParenthesis(ficlVm *vm) |
| { |
| ficlVmParseStringEx(vm, ')', 0); |
| } |
| |
| /* |
| * F E T C H & S T O R E |
| */ |
| |
| /* |
| * i f C o I m |
| * IMMEDIATE |
| * Compiles code for a conditional branch into the dictionary |
| * and pushes the branch patch address on the stack for later |
| * patching by ELSE or THEN/ENDIF. |
| */ |
| static void |
| ficlPrimitiveIfCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionBranch0ParenWithCheck); |
| markBranch(dictionary, vm, origTag); |
| ficlDictionaryAppendUnsigned(dictionary, 1); |
| } |
| |
| /* |
| * e l s e C o I m |
| * |
| * IMMEDIATE -- compiles an "else"... |
| * 1) FICL_VM_STATE_COMPILE a branch and a patch address; |
| * the address gets patched |
| * by "endif" to point past the "else" code. |
| * 2) Pop the the "if" patch address |
| * 3) Patch the "if" branch to point to the current FICL_VM_STATE_COMPILE |
| * address. |
| * 4) Push the "else" patch address. ("endif" patches this to jump past |
| * the "else" code. |
| */ |
| static void |
| ficlPrimitiveElseCoIm(ficlVm *vm) |
| { |
| ficlCell *patchAddr; |
| ficlInteger offset; |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| /* (1) FICL_VM_STATE_COMPILE branch runtime */ |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionBranchParenWithCheck); |
| |
| matchControlTag(vm, origTag); |
| /* (2) pop "if" patch addr */ |
| patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack); |
| markBranch(dictionary, vm, origTag); /* (4) push "else" patch addr */ |
| |
| /* (1) FICL_VM_STATE_COMPILE patch placeholder */ |
| ficlDictionaryAppendUnsigned(dictionary, 1); |
| offset = dictionary->here - patchAddr; |
| (*patchAddr).i = offset; /* (3) Patch "if" */ |
| } |
| |
| /* |
| * e n d i f C o I m |
| */ |
| static void |
| ficlPrimitiveEndifCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| resolveForwardBranch(dictionary, vm, origTag); |
| } |
| |
| /* |
| * c a s e C o I m |
| * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY |
| * |
| * |
| * At FICL_VM_STATE_COMPILE-time, a CASE-SYS (see DPANS94 6.2.0873) looks |
| * like this: |
| * i*addr i caseTag |
| * and an OF-SYS (see DPANS94 6.2.1950) looks like this: |
| * i*addr i caseTag addr ofTag |
| * The integer under caseTag is the count of fixup addresses that branch |
| * to ENDCASE. |
| */ |
| static void |
| ficlPrimitiveCaseCoIm(ficlVm *vm) |
| { |
| FICL_STACK_CHECK(vm->dataStack, 0, 2); |
| |
| ficlStackPushUnsigned(vm->dataStack, 0); |
| markControlTag(vm, caseTag); |
| } |
| |
| /* |
| * e n d c a s eC o I m |
| * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY |
| */ |
| static void |
| ficlPrimitiveEndcaseCoIm(ficlVm *vm) |
| { |
| ficlUnsigned fixupCount; |
| ficlDictionary *dictionary; |
| ficlCell *patchAddr; |
| ficlInteger offset; |
| |
| /* |
| * if the last OF ended with FALLTHROUGH, |
| * just add the FALLTHROUGH fixup to the |
| * ENDOF fixups |
| */ |
| if (ficlStackGetTop(vm->dataStack).p == fallthroughTag) { |
| matchControlTag(vm, fallthroughTag); |
| patchAddr = ficlStackPopPointer(vm->dataStack); |
| matchControlTag(vm, caseTag); |
| fixupCount = ficlStackPopUnsigned(vm->dataStack); |
| ficlStackPushPointer(vm->dataStack, patchAddr); |
| ficlStackPushUnsigned(vm->dataStack, fixupCount + 1); |
| markControlTag(vm, caseTag); |
| } |
| |
| matchControlTag(vm, caseTag); |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| fixupCount = ficlStackPopUnsigned(vm->dataStack); |
| FICL_STACK_CHECK(vm->dataStack, fixupCount, 0); |
| |
| dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstructionDrop); |
| |
| while (fixupCount--) { |
| patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack); |
| offset = dictionary->here - patchAddr; |
| (*patchAddr).i = offset; |
| } |
| } |
| |
| /* |
| * o f C o I m |
| * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY |
| */ |
| static void |
| ficlPrimitiveOfCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlCell *fallthroughFixup = NULL; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 3); |
| |
| if (ficlStackGetTop(vm->dataStack).p == fallthroughTag) { |
| matchControlTag(vm, fallthroughTag); |
| fallthroughFixup = ficlStackPopPointer(vm->dataStack); |
| } |
| |
| matchControlTag(vm, caseTag); |
| |
| markControlTag(vm, caseTag); |
| |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstructionOfParen); |
| markBranch(dictionary, vm, ofTag); |
| ficlDictionaryAppendUnsigned(dictionary, 2); |
| |
| if (fallthroughFixup != NULL) { |
| ficlInteger offset = dictionary->here - fallthroughFixup; |
| (*fallthroughFixup).i = offset; |
| } |
| } |
| |
| /* |
| * e n d o f C o I m |
| * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY |
| */ |
| static void |
| ficlPrimitiveEndofCoIm(ficlVm *vm) |
| { |
| ficlCell *patchAddr; |
| ficlUnsigned fixupCount; |
| ficlInteger offset; |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| FICL_STACK_CHECK(vm->dataStack, 4, 3); |
| |
| /* ensure we're in an OF, */ |
| matchControlTag(vm, ofTag); |
| |
| /* grab the address of the branch location after the OF */ |
| patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack); |
| /* ensure we're also in a "case" */ |
| matchControlTag(vm, caseTag); |
| /* grab the current number of ENDOF fixups */ |
| fixupCount = ficlStackPopUnsigned(vm->dataStack); |
| |
| /* FICL_VM_STATE_COMPILE branch runtime */ |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionBranchParenWithCheck); |
| |
| /* |
| * push a new ENDOF fixup, the updated count of ENDOF fixups, |
| * and the caseTag |
| */ |
| ficlStackPushPointer(vm->dataStack, dictionary->here); |
| ficlStackPushUnsigned(vm->dataStack, fixupCount + 1); |
| markControlTag(vm, caseTag); |
| |
| /* reserve space for the ENDOF fixup */ |
| ficlDictionaryAppendUnsigned(dictionary, 2); |
| |
| /* and patch the original OF */ |
| offset = dictionary->here - patchAddr; |
| (*patchAddr).i = offset; |
| } |
| |
| /* |
| * f a l l t h r o u g h C o I m |
| * IMMEDIATE FICL_VM_STATE_COMPILE-ONLY |
| */ |
| static void |
| ficlPrimitiveFallthroughCoIm(ficlVm *vm) |
| { |
| ficlCell *patchAddr; |
| ficlInteger offset; |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| FICL_STACK_CHECK(vm->dataStack, 4, 3); |
| |
| /* ensure we're in an OF, */ |
| matchControlTag(vm, ofTag); |
| /* grab the address of the branch location after the OF */ |
| patchAddr = (ficlCell *)ficlStackPopPointer(vm->dataStack); |
| /* ensure we're also in a "case" */ |
| matchControlTag(vm, caseTag); |
| |
| /* okay, here we go. put the case tag back. */ |
| markControlTag(vm, caseTag); |
| |
| /* FICL_VM_STATE_COMPILE branch runtime */ |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionBranchParenWithCheck); |
| |
| /* push a new FALLTHROUGH fixup and the fallthroughTag */ |
| ficlStackPushPointer(vm->dataStack, dictionary->here); |
| markControlTag(vm, fallthroughTag); |
| |
| /* reserve space for the FALLTHROUGH fixup */ |
| ficlDictionaryAppendUnsigned(dictionary, 2); |
| |
| /* and patch the original OF */ |
| offset = dictionary->here - patchAddr; |
| (*patchAddr).i = offset; |
| } |
| |
| /* |
| * h a s h |
| * hash ( c-addr u -- code) |
| * calculates hashcode of specified string and leaves it on the stack |
| */ |
| static void |
| ficlPrimitiveHash(ficlVm *vm) |
| { |
| ficlString s; |
| |
| FICL_STRING_SET_LENGTH(s, ficlStackPopUnsigned(vm->dataStack)); |
| FICL_STRING_SET_POINTER(s, ficlStackPopPointer(vm->dataStack)); |
| ficlStackPushUnsigned(vm->dataStack, ficlHashCode(s)); |
| } |
| |
| /* |
| * i n t e r p r e t |
| * This is the "user interface" of a Forth. It does the following: |
| * while there are words in the VM's Text Input Buffer |
| * Copy next word into the pad (ficlVmGetWord) |
| * Attempt to find the word in the dictionary (ficlDictionaryLookup) |
| * If successful, execute the word. |
| * Otherwise, attempt to convert the word to a number (isNumber) |
| * If successful, push the number onto the parameter stack. |
| * Otherwise, print an error message and exit loop... |
| * End Loop |
| * |
| * From the standard, section 3.4 |
| * Text interpretation (see 6.1.1360 EVALUATE and 6.1.2050 QUIT) shall |
| * repeat the following steps until either the parse area is empty or an |
| * ambiguous condition exists: |
| * a) Skip leading spaces and parse a name (see 3.4.1); |
| */ |
| static void |
| ficlPrimitiveInterpret(ficlVm *vm) |
| { |
| ficlString s; |
| int i; |
| ficlSystem *system; |
| |
| FICL_VM_ASSERT(vm, vm); |
| |
| system = vm->callback.system; |
| s = ficlVmGetWord0(vm); |
| |
| /* |
| * Get next word...if out of text, we're done. |
| */ |
| if (s.length == 0) { |
| ficlVmThrow(vm, FICL_VM_STATUS_OUT_OF_TEXT); |
| } |
| |
| /* |
| * Run the parse chain against the incoming token until somebody |
| * eats it. Otherwise emit an error message and give up. |
| */ |
| for (i = 0; i < FICL_MAX_PARSE_STEPS; i++) { |
| ficlWord *word = system->parseList[i]; |
| |
| if (word == NULL) |
| break; |
| |
| if (word->code == ficlPrimitiveParseStepParen) { |
| ficlParseStep pStep; |
| pStep = (ficlParseStep)(word->param->fn); |
| if ((*pStep)(vm, s)) |
| return; |
| } else { |
| ficlStackPushPointer(vm->dataStack, |
| FICL_STRING_GET_POINTER(s)); |
| ficlStackPushUnsigned(vm->dataStack, |
| FICL_STRING_GET_LENGTH(s)); |
| ficlVmExecuteXT(vm, word); |
| if (ficlStackPopInteger(vm->dataStack)) |
| return; |
| } |
| } |
| |
| ficlVmThrowError(vm, "%.*s not found", FICL_STRING_GET_LENGTH(s), |
| FICL_STRING_GET_POINTER(s)); |
| /* back to inner interpreter */ |
| } |
| |
| /* |
| * Surrogate precompiled parse step for ficlParseWord |
| * (this step is hard coded in FICL_VM_STATE_INTERPRET) |
| */ |
| static void |
| ficlPrimitiveLookup(ficlVm *vm) |
| { |
| ficlString name; |
| FICL_STRING_SET_LENGTH(name, ficlStackPopUnsigned(vm->dataStack)); |
| FICL_STRING_SET_POINTER(name, ficlStackPopPointer(vm->dataStack)); |
| ficlStackPushInteger(vm->dataStack, ficlVmParseWord(vm, name)); |
| } |
| |
| /* |
| * p a r e n P a r s e S t e p |
| * (parse-step) ( c-addr u -- flag ) |
| * runtime for a precompiled parse step - pop a counted string off the |
| * stack, run the parse step against it, and push the result flag (FICL_TRUE |
| * if success, FICL_FALSE otherwise). |
| */ |
| void |
| ficlPrimitiveParseStepParen(ficlVm *vm) |
| { |
| ficlString s; |
| ficlWord *word = vm->runningWord; |
| ficlParseStep pStep = (ficlParseStep)(word->param->fn); |
| |
| FICL_STRING_SET_LENGTH(s, ficlStackPopInteger(vm->dataStack)); |
| FICL_STRING_SET_POINTER(s, ficlStackPopPointer(vm->dataStack)); |
| |
| ficlStackPushInteger(vm->dataStack, (*pStep)(vm, s)); |
| } |
| |
| static void |
| ficlPrimitiveAddParseStep(ficlVm *vm) |
| { |
| ficlWord *pStep; |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| pStep = (ficlWord *)(ficlStackPop(vm->dataStack).p); |
| if ((pStep != NULL) && ficlDictionaryIsAWord(dictionary, pStep)) |
| ficlSystemAddParseStep(vm->callback.system, pStep); |
| } |
| |
| /* |
| * l i t e r a l I m |
| * |
| * IMMEDIATE code for "literal". This function gets a value from the stack |
| * and compiles it into the dictionary preceded by the code for "(literal)". |
| * IMMEDIATE |
| */ |
| void |
| ficlPrimitiveLiteralIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlInteger value; |
| |
| value = ficlStackPopInteger(vm->dataStack); |
| |
| switch (value) { |
| case 1: |
| case 2: |
| case 3: |
| case 4: |
| case 5: |
| case 6: |
| case 7: |
| case 8: |
| case 9: |
| case 10: |
| case 11: |
| case 12: |
| case 13: |
| case 14: |
| case 15: |
| case 16: |
| ficlDictionaryAppendUnsigned(dictionary, value); |
| break; |
| |
| case 0: |
| case -1: |
| case -2: |
| case -3: |
| case -4: |
| case -5: |
| case -6: |
| case -7: |
| case -8: |
| case -9: |
| case -10: |
| case -11: |
| case -12: |
| case -13: |
| case -14: |
| case -15: |
| case -16: |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstruction0 - value); |
| break; |
| |
| default: |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionLiteralParen); |
| ficlDictionaryAppendUnsigned(dictionary, value); |
| break; |
| } |
| } |
| |
| static void |
| ficlPrimitive2LiteralIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstruction2LiteralParen); |
| ficlDictionaryAppendCell(dictionary, ficlStackPop(vm->dataStack)); |
| ficlDictionaryAppendCell(dictionary, ficlStackPop(vm->dataStack)); |
| } |
| |
| /* |
| * D o / L o o p |
| * do -- IMMEDIATE FICL_VM_STATE_COMPILE ONLY |
| * Compiles code to initialize a loop: FICL_VM_STATE_COMPILE (do), |
| * allot space to hold the "leave" address, push a branch |
| * target address for the loop. |
| * (do) -- runtime for "do" |
| * pops index and limit from the p stack and moves them |
| * to the r stack, then skips to the loop body. |
| * loop -- IMMEDIATE FICL_VM_STATE_COMPILE ONLY |
| * +loop |
| * Compiles code for the test part of a loop: |
| * FICL_VM_STATE_COMPILE (loop), resolve forward branch from "do", and |
| * copy "here" address to the "leave" address allotted by "do" |
| * i,j,k -- FICL_VM_STATE_COMPILE ONLY |
| * Runtime: Push loop indices on param stack (i is innermost loop...) |
| * Note: each loop has three values on the return stack: |
| * ( R: leave limit index ) |
| * "leave" is the absolute address of the next ficlCell after the loop |
| * limit and index are the loop control variables. |
| * leave -- FICL_VM_STATE_COMPILE ONLY |
| * Runtime: pop the loop control variables, then pop the |
| * "leave" address and jump (absolute) there. |
| */ |
| static void |
| ficlPrimitiveDoCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstructionDoParen); |
| /* |
| * Allot space for a pointer to the end |
| * of the loop - "leave" uses this... |
| */ |
| markBranch(dictionary, vm, leaveTag); |
| ficlDictionaryAppendUnsigned(dictionary, 0); |
| /* |
| * Mark location of head of loop... |
| */ |
| markBranch(dictionary, vm, doTag); |
| } |
| |
| static void |
| ficlPrimitiveQDoCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstructionQDoParen); |
| /* |
| * Allot space for a pointer to the end |
| * of the loop - "leave" uses this... |
| */ |
| markBranch(dictionary, vm, leaveTag); |
| ficlDictionaryAppendUnsigned(dictionary, 0); |
| /* |
| * Mark location of head of loop... |
| */ |
| markBranch(dictionary, vm, doTag); |
| } |
| |
| |
| static void |
| ficlPrimitiveLoopCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstructionLoopParen); |
| resolveBackBranch(dictionary, vm, doTag); |
| resolveAbsBranch(dictionary, vm, leaveTag); |
| } |
| |
| static void |
| ficlPrimitivePlusLoopCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstructionPlusLoopParen); |
| resolveBackBranch(dictionary, vm, doTag); |
| resolveAbsBranch(dictionary, vm, leaveTag); |
| } |
| |
| /* |
| * v a r i a b l e |
| */ |
| static void |
| ficlPrimitiveVariable(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlString name = ficlVmGetWord(vm); |
| |
| ficlDictionaryAppendWord(dictionary, name, |
| (ficlPrimitive)ficlInstructionVariableParen, FICL_WORD_DEFAULT); |
| ficlVmDictionaryAllotCells(vm, dictionary, 1); |
| } |
| |
| static void |
| ficlPrimitive2Variable(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlString name = ficlVmGetWord(vm); |
| |
| ficlDictionaryAppendWord(dictionary, name, |
| (ficlPrimitive)ficlInstructionVariableParen, FICL_WORD_DEFAULT); |
| ficlVmDictionaryAllotCells(vm, dictionary, 2); |
| } |
| |
| /* |
| * b a s e & f r i e n d s |
| */ |
| static void |
| ficlPrimitiveBase(ficlVm *vm) |
| { |
| ficlCell *pBase, c; |
| |
| FICL_STACK_CHECK(vm->dataStack, 0, 1); |
| |
| pBase = (ficlCell *)(&vm->base); |
| c.p = pBase; |
| ficlStackPush(vm->dataStack, c); |
| } |
| |
| static void |
| ficlPrimitiveDecimal(ficlVm *vm) |
| { |
| vm->base = 10; |
| } |
| |
| |
| static void |
| ficlPrimitiveHex(ficlVm *vm) |
| { |
| vm->base = 16; |
| } |
| |
| /* |
| * a l l o t & f r i e n d s |
| */ |
| static void |
| ficlPrimitiveAllot(ficlVm *vm) |
| { |
| ficlDictionary *dictionary; |
| ficlInteger i; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| dictionary = ficlVmGetDictionary(vm); |
| i = ficlStackPopInteger(vm->dataStack); |
| |
| FICL_VM_DICTIONARY_CHECK(vm, dictionary, i); |
| |
| ficlVmDictionaryAllot(vm, dictionary, i); |
| } |
| |
| static void |
| ficlPrimitiveHere(ficlVm *vm) |
| { |
| ficlDictionary *dictionary; |
| |
| FICL_STACK_CHECK(vm->dataStack, 0, 1); |
| |
| dictionary = ficlVmGetDictionary(vm); |
| ficlStackPushPointer(vm->dataStack, dictionary->here); |
| } |
| |
| /* |
| * t i c k |
| * tick CORE ( "<spaces>name" -- xt ) |
| * Skip leading space delimiters. Parse name delimited by a space. Find |
| * name and return xt, the execution token for name. An ambiguous condition |
| * exists if name is not found. |
| */ |
| void |
| ficlPrimitiveTick(ficlVm *vm) |
| { |
| ficlWord *word = NULL; |
| ficlString name = ficlVmGetWord(vm); |
| |
| FICL_STACK_CHECK(vm->dataStack, 0, 1); |
| |
| word = ficlDictionaryLookup(ficlVmGetDictionary(vm), name); |
| if (!word) |
| ficlVmThrowError(vm, "%.*s not found", |
| FICL_STRING_GET_LENGTH(name), |
| FICL_STRING_GET_POINTER(name)); |
| ficlStackPushPointer(vm->dataStack, word); |
| } |
| |
| static void |
| ficlPrimitiveBracketTickCoIm(ficlVm *vm) |
| { |
| ficlPrimitiveTick(vm); |
| ficlPrimitiveLiteralIm(vm); |
| } |
| |
| /* |
| * p o s t p o n e |
| * Lookup the next word in the input stream and FICL_VM_STATE_COMPILE code to |
| * insert it into definitions created by the resulting word |
| * (defers compilation, even of immediate words) |
| */ |
| static void |
| ficlPrimitivePostponeCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlWord *word; |
| ficlWord *pComma = ficlSystemLookup(vm->callback.system, ","); |
| ficlCell c; |
| |
| FICL_VM_ASSERT(vm, pComma); |
| |
| ficlPrimitiveTick(vm); |
| word = ficlStackGetTop(vm->dataStack).p; |
| if (ficlWordIsImmediate(word)) { |
| ficlDictionaryAppendCell(dictionary, |
| ficlStackPop(vm->dataStack)); |
| } else { |
| ficlPrimitiveLiteralIm(vm); |
| c.p = pComma; |
| ficlDictionaryAppendCell(dictionary, c); |
| } |
| } |
| |
| /* |
| * e x e c u t e |
| * Pop an execution token (pointer to a word) off the stack and |
| * run it |
| */ |
| static void |
| ficlPrimitiveExecute(ficlVm *vm) |
| { |
| ficlWord *word; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| word = ficlStackPopPointer(vm->dataStack); |
| ficlVmExecuteWord(vm, word); |
| } |
| |
| /* |
| * i m m e d i a t e |
| * Make the most recently compiled word IMMEDIATE -- it executes even |
| * in FICL_VM_STATE_COMPILE state (most often used for control compiling words |
| * such as IF, THEN, etc) |
| */ |
| static void |
| ficlPrimitiveImmediate(ficlVm *vm) |
| { |
| FICL_IGNORE(vm); |
| ficlDictionarySetImmediate(ficlVmGetDictionary(vm)); |
| } |
| |
| static void |
| ficlPrimitiveCompileOnly(ficlVm *vm) |
| { |
| FICL_IGNORE(vm); |
| ficlDictionarySetFlags(ficlVmGetDictionary(vm), FICL_WORD_COMPILE_ONLY); |
| } |
| |
| static void |
| ficlPrimitiveSetObjectFlag(ficlVm *vm) |
| { |
| FICL_IGNORE(vm); |
| ficlDictionarySetFlags(ficlVmGetDictionary(vm), FICL_WORD_OBJECT); |
| } |
| |
| static void |
| ficlPrimitiveIsObject(ficlVm *vm) |
| { |
| ficlInteger flag; |
| ficlWord *word = (ficlWord *)ficlStackPopPointer(vm->dataStack); |
| |
| flag = ((word != NULL) && (word->flags & FICL_WORD_OBJECT))? |
| FICL_TRUE : FICL_FALSE; |
| |
| ficlStackPushInteger(vm->dataStack, flag); |
| } |
| |
| static void |
| ficlPrimitiveCountedStringQuoteIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| if (vm->state == FICL_VM_STATE_INTERPRET) { |
| ficlCountedString *counted = (ficlCountedString *) |
| dictionary->here; |
| |
| ficlVmGetString(vm, counted, '\"'); |
| ficlStackPushPointer(vm->dataStack, counted); |
| |
| /* |
| * move HERE past string so it doesn't get overwritten. --lch |
| */ |
| ficlVmDictionaryAllot(vm, dictionary, |
| counted->length + sizeof (ficlUnsigned8)); |
| } else { /* FICL_VM_STATE_COMPILE state */ |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionCStringLiteralParen); |
| dictionary->here = |
| FICL_POINTER_TO_CELL(ficlVmGetString(vm, |
| (ficlCountedString *)dictionary->here, '\"')); |
| ficlDictionaryAlign(dictionary); |
| } |
| } |
| |
| /* |
| * d o t Q u o t e |
| * IMMEDIATE word that compiles a string literal for later display |
| * FICL_VM_STATE_COMPILE fiStringLiteralParen, then copy the bytes of the |
| * string from the |
| * TIB to the dictionary. Backpatch the count byte and align the dictionary. |
| */ |
| static void |
| ficlPrimitiveDotQuoteCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlWord *pType = ficlSystemLookup(vm->callback.system, "type"); |
| ficlCell c; |
| |
| FICL_VM_ASSERT(vm, pType); |
| |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionStringLiteralParen); |
| dictionary->here = |
| FICL_POINTER_TO_CELL(ficlVmGetString(vm, |
| (ficlCountedString *)dictionary->here, '\"')); |
| ficlDictionaryAlign(dictionary); |
| c.p = pType; |
| ficlDictionaryAppendCell(dictionary, c); |
| } |
| |
| static void |
| ficlPrimitiveDotParen(ficlVm *vm) |
| { |
| char *from = ficlVmGetInBuf(vm); |
| char *stop = ficlVmGetInBufEnd(vm); |
| char *to = vm->pad; |
| char c; |
| |
| /* |
| * Note: the standard does not want leading spaces skipped. |
| */ |
| for (c = *from; (from != stop) && (c != ')'); c = *++from) |
| *to++ = c; |
| |
| *to = '\0'; |
| if ((from != stop) && (c == ')')) |
| from++; |
| |
| ficlVmTextOut(vm, vm->pad); |
| ficlVmUpdateTib(vm, from); |
| } |
| |
| /* |
| * s l i t e r a l |
| * STRING |
| * Interpretation: Interpretation semantics for this word are undefined. |
| * Compilation: ( c-addr1 u -- ) |
| * Append the run-time semantics given below to the current definition. |
| * Run-time: ( -- c-addr2 u ) |
| * Return c-addr2 u describing a string consisting of the characters |
| * specified by c-addr1 u during compilation. A program shall not alter |
| * the returned string. |
| */ |
| static void ficlPrimitiveSLiteralCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary; |
| char *from; |
| char *to; |
| ficlUnsigned length; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 0); |
| |
| dictionary = ficlVmGetDictionary(vm); |
| length = ficlStackPopUnsigned(vm->dataStack); |
| from = ficlStackPopPointer(vm->dataStack); |
| |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionStringLiteralParen); |
| to = (char *)dictionary->here; |
| *to++ = (char)length; |
| |
| for (; length > 0; --length) { |
| *to++ = *from++; |
| } |
| |
| *to++ = 0; |
| dictionary->here = FICL_POINTER_TO_CELL(ficlAlignPointer(to)); |
| } |
| |
| /* |
| * s t a t e |
| * Return the address of the VM's state member (must be sized the |
| * same as a ficlCell for this reason) |
| */ |
| static void ficlPrimitiveState(ficlVm *vm) |
| { |
| FICL_STACK_CHECK(vm->dataStack, 0, 1); |
| ficlStackPushPointer(vm->dataStack, &vm->state); |
| } |
| |
| /* |
| * c r e a t e . . . d o e s > |
| * Make a new word in the dictionary with the run-time effect of |
| * a variable (push my address), but with extra space allotted |
| * for use by does> . |
| */ |
| static void |
| ficlPrimitiveCreate(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlString name = ficlVmGetWord(vm); |
| |
| ficlDictionaryAppendWord(dictionary, name, |
| (ficlPrimitive)ficlInstructionCreateParen, FICL_WORD_DEFAULT); |
| ficlVmDictionaryAllotCells(vm, dictionary, 1); |
| } |
| |
| static void |
| ficlPrimitiveDoesCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| #if FICL_WANT_LOCALS |
| if (vm->callback.system->localsCount > 0) { |
| ficlDictionary *locals = |
| ficlSystemGetLocals(vm->callback.system); |
| ficlDictionaryEmpty(locals, locals->forthWordlist->size); |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionUnlinkParen); |
| } |
| |
| vm->callback.system->localsCount = 0; |
| #endif |
| FICL_IGNORE(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, ficlInstructionDoesParen); |
| } |
| |
| /* |
| * t o b o d y |
| * to-body CORE ( xt -- a-addr ) |
| * a-addr is the data-field address corresponding to xt. An ambiguous |
| * condition exists if xt is not for a word defined via CREATE. |
| */ |
| static void |
| ficlPrimitiveToBody(ficlVm *vm) |
| { |
| ficlWord *word; |
| FICL_STACK_CHECK(vm->dataStack, 1, 1); |
| |
| word = ficlStackPopPointer(vm->dataStack); |
| ficlStackPushPointer(vm->dataStack, word->param + 1); |
| } |
| |
| /* |
| * from-body Ficl ( a-addr -- xt ) |
| * Reverse effect of >body |
| */ |
| static void |
| ficlPrimitiveFromBody(ficlVm *vm) |
| { |
| char *ptr; |
| FICL_STACK_CHECK(vm->dataStack, 1, 1); |
| |
| ptr = (char *)ficlStackPopPointer(vm->dataStack) - sizeof (ficlWord); |
| ficlStackPushPointer(vm->dataStack, ptr); |
| } |
| |
| /* |
| * >name Ficl ( xt -- c-addr u ) |
| * Push the address and length of a word's name given its address |
| * xt. |
| */ |
| static void |
| ficlPrimitiveToName(ficlVm *vm) |
| { |
| ficlWord *word; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 2); |
| |
| word = ficlStackPopPointer(vm->dataStack); |
| ficlStackPushPointer(vm->dataStack, word->name); |
| ficlStackPushUnsigned(vm->dataStack, word->length); |
| } |
| |
| static void |
| ficlPrimitiveLastWord(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlWord *wp = dictionary->smudge; |
| ficlCell c; |
| |
| FICL_VM_ASSERT(vm, wp); |
| |
| c.p = wp; |
| ficlVmPush(vm, c); |
| } |
| |
| /* |
| * l b r a c k e t e t c |
| */ |
| static void |
| ficlPrimitiveLeftBracketCoIm(ficlVm *vm) |
| { |
| vm->state = FICL_VM_STATE_INTERPRET; |
| } |
| |
| static void |
| ficlPrimitiveRightBracket(ficlVm *vm) |
| { |
| vm->state = FICL_VM_STATE_COMPILE; |
| } |
| |
| /* |
| * p i c t u r e d n u m e r i c w o r d s |
| * |
| * less-number-sign CORE ( -- ) |
| * Initialize the pictured numeric output conversion process. |
| * (clear the pad) |
| */ |
| static void |
| ficlPrimitiveLessNumberSign(ficlVm *vm) |
| { |
| ficlCountedString *counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad); |
| counted->length = 0; |
| } |
| |
| /* |
| * number-sign CORE ( ud1 -- ud2 ) |
| * Divide ud1 by the number in BASE giving the quotient ud2 and the remainder |
| * n. (n is the least-significant digit of ud1.) Convert n to external form |
| * and add the resulting character to the beginning of the pictured numeric |
| * output string. An ambiguous condition exists if # executes outside of a |
| * <# #> delimited number conversion. |
| */ |
| static void |
| ficlPrimitiveNumberSign(ficlVm *vm) |
| { |
| ficlCountedString *counted; |
| ficl2Unsigned u; |
| ficl2UnsignedQR uqr; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 2); |
| |
| counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad); |
| u = ficlStackPop2Unsigned(vm->dataStack); |
| uqr = ficl2UnsignedDivide(u, (ficlUnsigned16)(vm->base)); |
| counted->text[counted->length++] = ficlDigitToCharacter(uqr.remainder); |
| ficlStackPush2Unsigned(vm->dataStack, uqr.quotient); |
| } |
| |
| /* |
| * number-sign-greater CORE ( xd -- c-addr u ) |
| * Drop xd. Make the pictured numeric output string available as a character |
| * string. c-addr and u specify the resulting character string. A program |
| * may replace characters within the string. |
| */ |
| static void |
| ficlPrimitiveNumberSignGreater(ficlVm *vm) |
| { |
| ficlCountedString *counted; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 2); |
| |
| counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad); |
| counted->text[counted->length] = 0; |
| ficlStringReverse(counted->text); |
| ficlStackDrop(vm->dataStack, 2); |
| ficlStackPushPointer(vm->dataStack, counted->text); |
| ficlStackPushUnsigned(vm->dataStack, counted->length); |
| } |
| |
| /* |
| * number-sign-s CORE ( ud1 -- ud2 ) |
| * Convert one digit of ud1 according to the rule for #. Continue conversion |
| * until the quotient is zero. ud2 is zero. An ambiguous condition exists if |
| * #S executes outside of a <# #> delimited number conversion. |
| * TO DO: presently does not use ud1 hi ficlCell - use it! |
| */ |
| static void |
| ficlPrimitiveNumberSignS(ficlVm *vm) |
| { |
| ficlCountedString *counted; |
| ficl2Unsigned u; |
| ficl2UnsignedQR uqr; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 2); |
| |
| counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad); |
| u = ficlStackPop2Unsigned(vm->dataStack); |
| |
| do { |
| uqr = ficl2UnsignedDivide(u, (ficlUnsigned16)(vm->base)); |
| counted->text[counted->length++] = |
| ficlDigitToCharacter(uqr.remainder); |
| u = uqr.quotient; |
| } while (FICL_2UNSIGNED_NOT_ZERO(u)); |
| |
| ficlStackPush2Unsigned(vm->dataStack, u); |
| } |
| |
| /* |
| * HOLD CORE ( char -- ) |
| * Add char to the beginning of the pictured numeric output string. |
| * An ambiguous condition exists if HOLD executes outside of a <# #> |
| * delimited number conversion. |
| */ |
| static void |
| ficlPrimitiveHold(ficlVm *vm) |
| { |
| ficlCountedString *counted; |
| int i; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad); |
| i = ficlStackPopInteger(vm->dataStack); |
| counted->text[counted->length++] = (char)i; |
| } |
| |
| /* |
| * SIGN CORE ( n -- ) |
| * If n is negative, add a minus sign to the beginning of the pictured |
| * numeric output string. An ambiguous condition exists if SIGN |
| * executes outside of a <# #> delimited number conversion. |
| */ |
| static void |
| ficlPrimitiveSign(ficlVm *vm) |
| { |
| ficlCountedString *counted; |
| int i; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| counted = FICL_POINTER_TO_COUNTED_STRING(vm->pad); |
| i = ficlStackPopInteger(vm->dataStack); |
| if (i < 0) |
| counted->text[counted->length++] = '-'; |
| } |
| |
| /* |
| * t o N u m b e r |
| * to-number CORE ( ud1 c-addr1 u1 -- ud2 c-addr2 u2 ) |
| * ud2 is the unsigned result of converting the characters within the |
| * string specified by c-addr1 u1 into digits, using the number in BASE, |
| * and adding each into ud1 after multiplying ud1 by the number in BASE. |
| * Conversion continues left-to-right until a character that is not |
| * convertible, including any + or -, is encountered or the string is |
| * entirely converted. c-addr2 is the location of the first unconverted |
| * character or the first character past the end of the string if the string |
| * was entirely converted. u2 is the number of unconverted characters in the |
| * string. An ambiguous condition exists if ud2 overflows during the |
| * conversion. |
| */ |
| static void |
| ficlPrimitiveToNumber(ficlVm *vm) |
| { |
| ficlUnsigned length; |
| char *trace; |
| ficl2Unsigned accumulator; |
| ficlUnsigned base = vm->base; |
| ficlUnsigned c; |
| ficlUnsigned digit; |
| |
| FICL_STACK_CHECK(vm->dataStack, 4, 4); |
| |
| length = ficlStackPopUnsigned(vm->dataStack); |
| trace = (char *)ficlStackPopPointer(vm->dataStack); |
| accumulator = ficlStackPop2Unsigned(vm->dataStack); |
| |
| for (c = *trace; length > 0; c = *++trace, length--) { |
| if (c < '0') |
| break; |
| |
| digit = c - '0'; |
| |
| if (digit > 9) |
| digit = tolower(c) - 'a' + 10; |
| /* |
| * Note: following test also catches chars between 9 and a |
| * because 'digit' is unsigned! |
| */ |
| if (digit >= base) |
| break; |
| |
| accumulator = ficl2UnsignedMultiplyAccumulate(accumulator, |
| base, digit); |
| } |
| |
| ficlStackPush2Unsigned(vm->dataStack, accumulator); |
| ficlStackPushPointer(vm->dataStack, trace); |
| ficlStackPushUnsigned(vm->dataStack, length); |
| } |
| |
| /* |
| * q u i t & a b o r t |
| * quit CORE ( -- ) ( R: i*x -- ) |
| * Empty the return stack, store zero in SOURCE-ID if it is present, make |
| * the user input device the input source, and enter interpretation state. |
| * Do not display a message. Repeat the following: |
| * |
| * Accept a line from the input source into the input buffer, set >IN to |
| * zero, and FICL_VM_STATE_INTERPRET. |
| * Display the implementation-defined system prompt if in |
| * interpretation state, all processing has been completed, and no |
| * ambiguous condition exists. |
| */ |
| static void |
| ficlPrimitiveQuit(ficlVm *vm) |
| { |
| ficlVmThrow(vm, FICL_VM_STATUS_QUIT); |
| } |
| |
| static void |
| ficlPrimitiveAbort(ficlVm *vm) |
| { |
| ficlVmThrow(vm, FICL_VM_STATUS_ABORT); |
| } |
| |
| /* |
| * a c c e p t |
| * accept CORE ( c-addr +n1 -- +n2 ) |
| * Receive a string of at most +n1 characters. An ambiguous condition |
| * exists if +n1 is zero or greater than 32,767. Display graphic characters |
| * as they are received. A program that depends on the presence or absence |
| * of non-graphic characters in the string has an environmental dependency. |
| * The editing functions, if any, that the system performs in order to |
| * construct the string are implementation-defined. |
| * |
| * (Although the standard text doesn't say so, I assume that the intent |
| * of 'accept' is to store the string at the address specified on |
| * the stack.) |
| * |
| * NOTE: getchar() is used there as its present both in loader and |
| * userland; however, the more correct solution would be to set |
| * terminal to raw mode for userland. |
| */ |
| static void |
| ficlPrimitiveAccept(ficlVm *vm) |
| { |
| ficlUnsigned size; |
| char *address; |
| int c; |
| ficlUnsigned length = 0; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 1); |
| |
| size = ficlStackPopInteger(vm->dataStack); |
| address = ficlStackPopPointer(vm->dataStack); |
| |
| while (size != length) { |
| c = getchar(); |
| if (c == '\n' || c == '\r') |
| break; |
| address[length++] = c; |
| } |
| ficlStackPushInteger(vm->dataStack, length); |
| } |
| |
| /* |
| * a l i g n |
| * 6.1.0705 ALIGN CORE ( -- ) |
| * If the data-space pointer is not aligned, reserve enough space to |
| * align it. |
| */ |
| static void |
| ficlPrimitiveAlign(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| FICL_IGNORE(vm); |
| ficlDictionaryAlign(dictionary); |
| } |
| |
| /* |
| * a l i g n e d |
| */ |
| static void |
| ficlPrimitiveAligned(ficlVm *vm) |
| { |
| void *addr; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 1); |
| |
| addr = ficlStackPopPointer(vm->dataStack); |
| ficlStackPushPointer(vm->dataStack, ficlAlignPointer(addr)); |
| } |
| |
| /* |
| * b e g i n & f r i e n d s |
| * Indefinite loop control structures |
| * A.6.1.0760 BEGIN |
| * Typical use: |
| * : X ... BEGIN ... test UNTIL ; |
| * or |
| * : X ... BEGIN ... test WHILE ... REPEAT ; |
| */ |
| static void |
| ficlPrimitiveBeginCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| markBranch(dictionary, vm, destTag); |
| } |
| |
| static void |
| ficlPrimitiveUntilCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionBranch0ParenWithCheck); |
| resolveBackBranch(dictionary, vm, destTag); |
| } |
| |
| static void |
| ficlPrimitiveWhileCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 5); |
| |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionBranch0ParenWithCheck); |
| markBranch(dictionary, vm, origTag); |
| |
| /* equivalent to 2swap */ |
| ficlStackRoll(vm->dataStack, 3); |
| ficlStackRoll(vm->dataStack, 3); |
| |
| ficlDictionaryAppendUnsigned(dictionary, 1); |
| } |
| |
| static void |
| ficlPrimitiveRepeatCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionBranchParenWithCheck); |
| /* expect "begin" branch marker */ |
| resolveBackBranch(dictionary, vm, destTag); |
| /* expect "while" branch marker */ |
| resolveForwardBranch(dictionary, vm, origTag); |
| } |
| |
| static void |
| ficlPrimitiveAgainCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionBranchParenWithCheck); |
| /* expect "begin" branch marker */ |
| resolveBackBranch(dictionary, vm, destTag); |
| } |
| |
| /* |
| * c h a r & f r i e n d s |
| * 6.1.0895 CHAR CORE ( "<spaces>name" -- char ) |
| * Skip leading space delimiters. Parse name delimited by a space. |
| * Put the value of its first character onto the stack. |
| * |
| * bracket-char CORE |
| * Interpretation: Interpretation semantics for this word are undefined. |
| * Compilation: ( "<spaces>name" -- ) |
| * Skip leading space delimiters. Parse name delimited by a space. |
| * Append the run-time semantics given below to the current definition. |
| * Run-time: ( -- char ) |
| * Place char, the value of the first character of name, on the stack. |
| */ |
| static void |
| ficlPrimitiveChar(ficlVm *vm) |
| { |
| ficlString s; |
| |
| FICL_STACK_CHECK(vm->dataStack, 0, 1); |
| |
| s = ficlVmGetWord(vm); |
| ficlStackPushUnsigned(vm->dataStack, (ficlUnsigned)(s.text[0])); |
| } |
| |
| static void |
| ficlPrimitiveCharCoIm(ficlVm *vm) |
| { |
| ficlPrimitiveChar(vm); |
| ficlPrimitiveLiteralIm(vm); |
| } |
| |
| /* |
| * c h a r P l u s |
| * char-plus CORE ( c-addr1 -- c-addr2 ) |
| * Add the size in address units of a character to c-addr1, giving c-addr2. |
| */ |
| static void |
| ficlPrimitiveCharPlus(ficlVm *vm) |
| { |
| char *p; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 1); |
| |
| p = ficlStackPopPointer(vm->dataStack); |
| ficlStackPushPointer(vm->dataStack, p + 1); |
| } |
| |
| /* |
| * c h a r s |
| * chars CORE ( n1 -- n2 ) |
| * n2 is the size in address units of n1 characters. |
| * For most processors, this function can be a no-op. To guarantee |
| * portability, we'll multiply by sizeof (char). |
| */ |
| #if defined(_M_IX86) |
| #pragma warning(disable: 4127) |
| #endif |
| static void |
| ficlPrimitiveChars(ficlVm *vm) |
| { |
| if (sizeof (char) > 1) { |
| ficlInteger i; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 1); |
| |
| i = ficlStackPopInteger(vm->dataStack); |
| ficlStackPushInteger(vm->dataStack, i * sizeof (char)); |
| } |
| /* otherwise no-op! */ |
| } |
| #if defined(_M_IX86) |
| #pragma warning(default: 4127) |
| #endif |
| |
| /* |
| * c o u n t |
| * COUNT CORE ( c-addr1 -- c-addr2 u ) |
| * Return the character string specification for the counted string stored |
| * at c-addr1. c-addr2 is the address of the first character after c-addr1. |
| * u is the contents of the character at c-addr1, which is the length in |
| * characters of the string at c-addr2. |
| */ |
| static void |
| ficlPrimitiveCount(ficlVm *vm) |
| { |
| ficlCountedString *counted; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 2); |
| |
| counted = ficlStackPopPointer(vm->dataStack); |
| ficlStackPushPointer(vm->dataStack, counted->text); |
| ficlStackPushUnsigned(vm->dataStack, counted->length); |
| } |
| |
| /* |
| * e n v i r o n m e n t ? |
| * environment-query CORE ( c-addr u -- FICL_FALSE | i*x FICL_TRUE ) |
| * c-addr is the address of a character string and u is the string's |
| * character count. u may have a value in the range from zero to an |
| * implementation-defined maximum which shall not be less than 31. The |
| * character string should contain a keyword from 3.2.6 Environmental |
| * queries or the optional word sets to be checked for correspondence |
| * with an attribute of the present environment. If the system treats the |
| * attribute as unknown, the returned flag is FICL_FALSE; otherwise, the flag |
| * is FICL_TRUE and the i*x returned is of the type specified in the table for |
| * the attribute queried. |
| */ |
| static void |
| ficlPrimitiveEnvironmentQ(ficlVm *vm) |
| { |
| ficlDictionary *environment; |
| ficlWord *word; |
| ficlString name; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 1); |
| |
| environment = vm->callback.system->environment; |
| name.length = ficlStackPopUnsigned(vm->dataStack); |
| name.text = ficlStackPopPointer(vm->dataStack); |
| |
| word = ficlDictionaryLookup(environment, name); |
| |
| if (word != NULL) { |
| ficlVmExecuteWord(vm, word); |
| ficlStackPushInteger(vm->dataStack, FICL_TRUE); |
| } else { |
| ficlStackPushInteger(vm->dataStack, FICL_FALSE); |
| } |
| } |
| |
| /* |
| * e v a l u a t e |
| * EVALUATE CORE ( i*x c-addr u -- j*x ) |
| * Save the current input source specification. Store minus-one (-1) in |
| * SOURCE-ID if it is present. Make the string described by c-addr and u |
| * both the input source and input buffer, set >IN to zero, and |
| * FICL_VM_STATE_INTERPRET. |
| * When the parse area is empty, restore the prior input source |
| * specification. Other stack effects are due to the words EVALUATEd. |
| */ |
| static void |
| ficlPrimitiveEvaluate(ficlVm *vm) |
| { |
| ficlCell id; |
| int result; |
| ficlString string; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 0); |
| |
| FICL_STRING_SET_LENGTH(string, ficlStackPopUnsigned(vm->dataStack)); |
| FICL_STRING_SET_POINTER(string, ficlStackPopPointer(vm->dataStack)); |
| |
| id = vm->sourceId; |
| vm->sourceId.i = -1; |
| result = ficlVmExecuteString(vm, string); |
| vm->sourceId = id; |
| if (result != FICL_VM_STATUS_OUT_OF_TEXT) |
| ficlVmThrow(vm, result); |
| } |
| |
| /* |
| * s t r i n g q u o t e |
| * Interpreting: get string delimited by a quote from the input stream, |
| * copy to a scratch area, and put its count and address on the stack. |
| * Compiling: FICL_VM_STATE_COMPILE code to push the address and count |
| * of a string literal, FICL_VM_STATE_COMPILE the string from the input |
| * stream, and align the dictionary pointer. |
| */ |
| static void |
| ficlPrimitiveStringQuoteIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| |
| if (vm->state == FICL_VM_STATE_INTERPRET) { |
| ficlCountedString *counted; |
| counted = (ficlCountedString *)dictionary->here; |
| ficlVmGetString(vm, counted, '\"'); |
| ficlStackPushPointer(vm->dataStack, counted->text); |
| ficlStackPushUnsigned(vm->dataStack, counted->length); |
| } else { /* FICL_VM_STATE_COMPILE state */ |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionStringLiteralParen); |
| dictionary->here = FICL_POINTER_TO_CELL( |
| ficlVmGetString(vm, (ficlCountedString *)dictionary->here, |
| '\"')); |
| ficlDictionaryAlign(dictionary); |
| } |
| } |
| |
| /* |
| * t y p e |
| * Pop count and char address from stack and print the designated string. |
| */ |
| static void |
| ficlPrimitiveType(ficlVm *vm) |
| { |
| ficlUnsigned length; |
| char *s; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 0); |
| |
| length = ficlStackPopUnsigned(vm->dataStack); |
| s = ficlStackPopPointer(vm->dataStack); |
| |
| if ((s == NULL) || (length == 0)) |
| return; |
| |
| /* |
| * Since we don't have an output primitive for a counted string |
| * (oops), make sure the string is null terminated. If not, copy |
| * and terminate it. |
| */ |
| if (s[length] != 0) { |
| char *here = (char *)ficlVmGetDictionary(vm)->here; |
| if (s != here) |
| strncpy(here, s, length); |
| |
| here[length] = '\0'; |
| s = here; |
| } |
| |
| ficlVmTextOut(vm, s); |
| } |
| |
| /* |
| * w o r d |
| * word CORE ( char "<chars>ccc<char>" -- c-addr ) |
| * Skip leading delimiters. Parse characters ccc delimited by char. An |
| * ambiguous condition exists if the length of the parsed string is greater |
| * than the implementation-defined length of a counted string. |
| * |
| * c-addr is the address of a transient region containing the parsed word |
| * as a counted string. If the parse area was empty or contained no |
| * characters other than the delimiter, the resulting string has a zero |
| * length. A space, not included in the length, follows the string. A |
| * program may replace characters within the string. |
| * NOTE! Ficl also NULL-terminates the dest string. |
| */ |
| static void |
| ficlPrimitiveWord(ficlVm *vm) |
| { |
| ficlCountedString *counted; |
| char delim; |
| ficlString name; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 1); |
| |
| counted = (ficlCountedString *)vm->pad; |
| delim = (char)ficlStackPopInteger(vm->dataStack); |
| name = ficlVmParseStringEx(vm, delim, 1); |
| |
| if (FICL_STRING_GET_LENGTH(name) > FICL_PAD_SIZE - 1) |
| FICL_STRING_SET_LENGTH(name, FICL_PAD_SIZE - 1); |
| |
| counted->length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(name); |
| strncpy(counted->text, FICL_STRING_GET_POINTER(name), |
| FICL_STRING_GET_LENGTH(name)); |
| |
| /* |
| * store an extra space at the end of the primitive... |
| * why? dunno yet. Guy Carver did it. |
| */ |
| counted->text[counted->length] = ' '; |
| counted->text[counted->length + 1] = 0; |
| |
| ficlStackPushPointer(vm->dataStack, counted); |
| } |
| |
| /* |
| * p a r s e - w o r d |
| * Ficl PARSE-WORD ( <spaces>name -- c-addr u ) |
| * Skip leading spaces and parse name delimited by a space. c-addr is the |
| * address within the input buffer and u is the length of the selected |
| * string. If the parse area is empty, the resulting string has a zero length. |
| */ |
| static void ficlPrimitiveParseNoCopy(ficlVm *vm) |
| { |
| ficlString s; |
| |
| FICL_STACK_CHECK(vm->dataStack, 0, 2); |
| |
| s = ficlVmGetWord0(vm); |
| ficlStackPushPointer(vm->dataStack, FICL_STRING_GET_POINTER(s)); |
| ficlStackPushUnsigned(vm->dataStack, FICL_STRING_GET_LENGTH(s)); |
| } |
| |
| /* |
| * p a r s e |
| * CORE EXT ( char "ccc<char>" -- c-addr u ) |
| * Parse ccc delimited by the delimiter char. |
| * c-addr is the address (within the input buffer) and u is the length of |
| * the parsed string. If the parse area was empty, the resulting string has |
| * a zero length. |
| * NOTE! PARSE differs from WORD: it does not skip leading delimiters. |
| */ |
| static void |
| ficlPrimitiveParse(ficlVm *vm) |
| { |
| ficlString s; |
| char delim; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 2); |
| |
| delim = (char)ficlStackPopInteger(vm->dataStack); |
| |
| s = ficlVmParseStringEx(vm, delim, 0); |
| ficlStackPushPointer(vm->dataStack, FICL_STRING_GET_POINTER(s)); |
| ficlStackPushUnsigned(vm->dataStack, FICL_STRING_GET_LENGTH(s)); |
| } |
| |
| /* |
| * f i n d |
| * FIND CORE ( c-addr -- c-addr 0 | xt 1 | xt -1 ) |
| * Find the definition named in the counted string at c-addr. If the |
| * definition is not found, return c-addr and zero. If the definition is |
| * found, return its execution token xt. If the definition is immediate, |
| * also return one (1), otherwise also return minus-one (-1). For a given |
| * string, the values returned by FIND while compiling may differ from |
| * those returned while not compiling. |
| */ |
| static void |
| do_find(ficlVm *vm, ficlString name, void *returnForFailure) |
| { |
| ficlWord *word; |
| |
| word = ficlDictionaryLookup(ficlVmGetDictionary(vm), name); |
| if (word) { |
| ficlStackPushPointer(vm->dataStack, word); |
| ficlStackPushInteger(vm->dataStack, |
| (ficlWordIsImmediate(word) ? 1 : -1)); |
| } else { |
| ficlStackPushPointer(vm->dataStack, returnForFailure); |
| ficlStackPushUnsigned(vm->dataStack, 0); |
| } |
| } |
| |
| /* |
| * f i n d |
| * FIND CORE ( c-addr -- c-addr 0 | xt 1 | xt -1 ) |
| * Find the definition named in the counted string at c-addr. If the |
| * definition is not found, return c-addr and zero. If the definition is |
| * found, return its execution token xt. If the definition is immediate, |
| * also return one (1), otherwise also return minus-one (-1). For a given |
| * string, the values returned by FIND while compiling may differ from |
| * those returned while not compiling. |
| */ |
| static void |
| ficlPrimitiveCFind(ficlVm *vm) |
| { |
| ficlCountedString *counted; |
| ficlString name; |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 2); |
| |
| counted = ficlStackPopPointer(vm->dataStack); |
| FICL_STRING_SET_FROM_COUNTED_STRING(name, *counted); |
| do_find(vm, name, counted); |
| } |
| |
| /* |
| * s f i n d |
| * Ficl ( c-addr u -- 0 0 | xt 1 | xt -1 ) |
| * Like FIND, but takes "c-addr u" for the string. |
| */ |
| static void |
| ficlPrimitiveSFind(ficlVm *vm) |
| { |
| ficlString name; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 2); |
| |
| name.length = ficlStackPopInteger(vm->dataStack); |
| name.text = ficlStackPopPointer(vm->dataStack); |
| |
| do_find(vm, name, NULL); |
| } |
| |
| /* |
| * r e c u r s e |
| */ |
| static void |
| ficlPrimitiveRecurseCoIm(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlCell c; |
| |
| FICL_IGNORE(vm); |
| c.p = dictionary->smudge; |
| ficlDictionaryAppendCell(dictionary, c); |
| } |
| |
| /* |
| * s o u r c e |
| * CORE ( -- c-addr u ) |
| * c-addr is the address of, and u is the number of characters in, the |
| * input buffer. |
| */ |
| static void |
| ficlPrimitiveSource(ficlVm *vm) |
| { |
| FICL_STACK_CHECK(vm->dataStack, 0, 2); |
| |
| ficlStackPushPointer(vm->dataStack, vm->tib.text); |
| ficlStackPushInteger(vm->dataStack, ficlVmGetInBufLen(vm)); |
| } |
| |
| /* |
| * v e r s i o n |
| * non-standard... |
| */ |
| static void |
| ficlPrimitiveVersion(ficlVm *vm) |
| { |
| ficlVmTextOut(vm, "Ficl version " FICL_VERSION "\n"); |
| } |
| |
| /* |
| * t o I n |
| * to-in CORE |
| */ |
| static void |
| ficlPrimitiveToIn(ficlVm *vm) |
| { |
| FICL_STACK_CHECK(vm->dataStack, 0, 1); |
| |
| ficlStackPushPointer(vm->dataStack, &vm->tib.index); |
| } |
| |
| /* |
| * c o l o n N o N a m e |
| * CORE EXT ( C: -- colon-sys ) ( S: -- xt ) |
| * Create an unnamed colon definition and push its address. |
| * Change state to FICL_VM_STATE_COMPILE. |
| */ |
| static void |
| ficlPrimitiveColonNoName(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlWord *word; |
| ficlString name; |
| |
| FICL_STRING_SET_LENGTH(name, 0); |
| FICL_STRING_SET_POINTER(name, NULL); |
| |
| vm->state = FICL_VM_STATE_COMPILE; |
| word = ficlDictionaryAppendWord(dictionary, name, |
| (ficlPrimitive)ficlInstructionColonParen, |
| FICL_WORD_DEFAULT | FICL_WORD_SMUDGED); |
| |
| ficlStackPushPointer(vm->dataStack, word); |
| markControlTag(vm, colonTag); |
| } |
| |
| /* |
| * u s e r V a r i a b l e |
| * user ( u -- ) "<spaces>name" |
| * Get a name from the input stream and create a user variable |
| * with the name and the index supplied. The run-time effect |
| * of a user variable is to push the address of the indexed ficlCell |
| * in the running vm's user array. |
| * |
| * User variables are vm local cells. Each vm has an array of |
| * FICL_USER_CELLS of them when FICL_WANT_USER is nonzero. |
| * Ficl's user facility is implemented with two primitives, |
| * "user" and "(user)", a variable ("nUser") (in softcore.c) that |
| * holds the index of the next free user ficlCell, and a redefinition |
| * (also in softcore) of "user" that defines a user word and increments |
| * nUser. |
| */ |
| #if FICL_WANT_USER |
| static void |
| ficlPrimitiveUser(ficlVm *vm) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlString name = ficlVmGetWord(vm); |
| ficlCell c; |
| |
| c = ficlStackPop(vm->dataStack); |
| if (c.i >= FICL_USER_CELLS) { |
| ficlVmThrowError(vm, "Error - out of user space"); |
| } |
| |
| ficlDictionaryAppendWord(dictionary, name, |
| (ficlPrimitive)ficlInstructionUserParen, FICL_WORD_DEFAULT); |
| ficlDictionaryAppendCell(dictionary, c); |
| } |
| #endif |
| |
| #if FICL_WANT_LOCALS |
| /* |
| * Each local is recorded in a private locals dictionary as a |
| * word that does doLocalIm at runtime. DoLocalIm compiles code |
| * into the client definition to fetch the value of the |
| * corresponding local variable from the return stack. |
| * The private dictionary gets initialized at the end of each block |
| * that uses locals (in ; and does> for example). |
| */ |
| void |
| ficlLocalParenIm(ficlVm *vm, int isDouble, int isFloat) |
| { |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlInteger nLocal = vm->runningWord->param[0].i; |
| |
| #if !FICL_WANT_FLOAT |
| FICL_VM_ASSERT(vm, !isFloat); |
| /* get rid of unused parameter warning */ |
| isFloat = 0; |
| #endif /* FICL_WANT_FLOAT */ |
| |
| if (vm->state == FICL_VM_STATE_INTERPRET) { |
| ficlStack *stack; |
| #if FICL_WANT_FLOAT |
| if (isFloat) |
| stack = vm->floatStack; |
| else |
| #endif /* FICL_WANT_FLOAT */ |
| stack = vm->dataStack; |
| |
| ficlStackPush(stack, vm->returnStack->frame[nLocal]); |
| if (isDouble) |
| ficlStackPush(stack, vm->returnStack->frame[nLocal+1]); |
| } else { |
| ficlInstruction instruction; |
| ficlInteger appendLocalOffset; |
| #if FICL_WANT_FLOAT |
| if (isFloat) { |
| instruction = |
| (isDouble) ? ficlInstructionGetF2LocalParen : |
| ficlInstructionGetFLocalParen; |
| appendLocalOffset = FICL_TRUE; |
| } else |
| #endif /* FICL_WANT_FLOAT */ |
| if (nLocal == 0) { |
| instruction = (isDouble) ? ficlInstructionGet2Local0 : |
| ficlInstructionGetLocal0; |
| appendLocalOffset = FICL_FALSE; |
| } else if ((nLocal == 1) && !isDouble) { |
| instruction = ficlInstructionGetLocal1; |
| appendLocalOffset = FICL_FALSE; |
| } else { |
| instruction = |
| (isDouble) ? ficlInstructionGet2LocalParen : |
| ficlInstructionGetLocalParen; |
| appendLocalOffset = FICL_TRUE; |
| } |
| |
| ficlDictionaryAppendUnsigned(dictionary, instruction); |
| if (appendLocalOffset) |
| ficlDictionaryAppendUnsigned(dictionary, nLocal); |
| } |
| } |
| |
| static void |
| ficlPrimitiveDoLocalIm(ficlVm *vm) |
| { |
| ficlLocalParenIm(vm, 0, 0); |
| } |
| |
| static void |
| ficlPrimitiveDo2LocalIm(ficlVm *vm) |
| { |
| ficlLocalParenIm(vm, 1, 0); |
| } |
| |
| #if FICL_WANT_FLOAT |
| static void |
| ficlPrimitiveDoFLocalIm(ficlVm *vm) |
| { |
| ficlLocalParenIm(vm, 0, 1); |
| } |
| |
| static void |
| ficlPrimitiveDoF2LocalIm(ficlVm *vm) |
| { |
| ficlLocalParenIm(vm, 1, 1); |
| } |
| #endif /* FICL_WANT_FLOAT */ |
| |
| /* |
| * l o c a l P a r e n |
| * paren-local-paren LOCAL |
| * Interpretation: Interpretation semantics for this word are undefined. |
| * Execution: ( c-addr u -- ) |
| * When executed during compilation, (LOCAL) passes a message to the |
| * system that has one of two meanings. If u is non-zero, |
| * the message identifies a new local whose definition name is given by |
| * the string of characters identified by c-addr u. If u is zero, |
| * the message is last local and c-addr has no significance. |
| * |
| * The result of executing (LOCAL) during compilation of a definition is |
| * to create a set of named local identifiers, each of which is |
| * a definition name, that only have execution semantics within the scope |
| * of that definition's source. |
| * |
| * local Execution: ( -- x ) |
| * |
| * Push the local's value, x, onto the stack. The local's value is |
| * initialized as described in 13.3.3 Processing locals and may be |
| * changed by preceding the local's name with TO. An ambiguous condition |
| * exists when local is executed while in interpretation state. |
| */ |
| void |
| ficlLocalParen(ficlVm *vm, int isDouble, int isFloat) |
| { |
| ficlDictionary *dictionary; |
| ficlString name; |
| |
| FICL_STACK_CHECK(vm->dataStack, 2, 0); |
| |
| dictionary = ficlVmGetDictionary(vm); |
| FICL_STRING_SET_LENGTH(name, ficlStackPopUnsigned(vm->dataStack)); |
| FICL_STRING_SET_POINTER(name, |
| (char *)ficlStackPopPointer(vm->dataStack)); |
| |
| if (FICL_STRING_GET_LENGTH(name) > 0) { |
| /* |
| * add a local to the **locals** dictionary and |
| * update localsCount |
| */ |
| ficlPrimitive code; |
| ficlInstruction instruction; |
| ficlDictionary *locals; |
| |
| locals = ficlSystemGetLocals(vm->callback.system); |
| if (vm->callback.system->localsCount >= FICL_MAX_LOCALS) { |
| ficlVmThrowError(vm, "Error: out of local space"); |
| } |
| |
| #if !FICL_WANT_FLOAT |
| FICL_VM_ASSERT(vm, !isFloat); |
| /* get rid of unused parameter warning */ |
| isFloat = 0; |
| #else /* FICL_WANT_FLOAT */ |
| if (isFloat) { |
| if (isDouble) { |
| code = ficlPrimitiveDoF2LocalIm; |
| instruction = ficlInstructionToF2LocalParen; |
| } else { |
| code = ficlPrimitiveDoFLocalIm; |
| instruction = ficlInstructionToFLocalParen; |
| } |
| } else |
| #endif /* FICL_WANT_FLOAT */ |
| if (isDouble) { |
| code = ficlPrimitiveDo2LocalIm; |
| instruction = ficlInstructionTo2LocalParen; |
| } else { |
| code = ficlPrimitiveDoLocalIm; |
| instruction = ficlInstructionToLocalParen; |
| } |
| |
| ficlDictionaryAppendWord(locals, name, code, |
| FICL_WORD_COMPILE_ONLY_IMMEDIATE); |
| ficlDictionaryAppendUnsigned(locals, |
| vm->callback.system->localsCount); |
| |
| if (vm->callback.system->localsCount == 0) { |
| /* |
| * FICL_VM_STATE_COMPILE code to create a local |
| * stack frame |
| */ |
| ficlDictionaryAppendUnsigned(dictionary, |
| ficlInstructionLinkParen); |
| |
| /* save location in dictionary for #locals */ |
| vm->callback.system->localsFixup = dictionary->here; |
| ficlDictionaryAppendUnsigned(dictionary, |
| vm->callback.system->localsCount); |
| } |
| |
| ficlDictionaryAppendUnsigned(dictionary, instruction); |
| ficlDictionaryAppendUnsigned(dictionary, |
| vm->callback.system->localsCount); |
| |
| vm->callback.system->localsCount += (isDouble) ? 2 : 1; |
| } else if (vm->callback.system->localsCount > 0) { |
| /* write localsCount to (link) param area in dictionary */ |
| *(ficlInteger *)(vm->callback.system->localsFixup) = |
| vm->callback.system->localsCount; |
| } |
| } |
| |
| static void |
| ficlPrimitiveLocalParen(ficlVm *vm) |
| { |
| ficlLocalParen(vm, 0, 0); |
| } |
| |
| static void |
| ficlPrimitive2LocalParen(ficlVm *vm) |
| { |
| ficlLocalParen(vm, 1, 0); |
| } |
| #endif /* FICL_WANT_LOCALS */ |
| |
| /* |
| * t o V a l u e |
| * CORE EXT |
| * Interpretation: ( x "<spaces>name" -- ) |
| * Skip leading spaces and parse name delimited by a space. Store x in |
| * name. An ambiguous condition exists if name was not defined by VALUE. |
| * NOTE: In Ficl, VALUE is an alias of CONSTANT |
| */ |
| static void |
| ficlPrimitiveToValue(ficlVm *vm) |
| { |
| ficlString name = ficlVmGetWord(vm); |
| ficlDictionary *dictionary = ficlVmGetDictionary(vm); |
| ficlWord *word; |
| ficlInstruction instruction = 0; |
| ficlStack *stack; |
| ficlInteger isDouble; |
| #if FICL_WANT_LOCALS |
| ficlInteger nLocal; |
| ficlInteger appendLocalOffset; |
| ficlInteger isFloat; |
| #endif /* FICL_WANT_LOCALS */ |
| |
| #if FICL_WANT_LOCALS |
| if ((vm->callback.system->localsCount > 0) && |
| (vm->state == FICL_VM_STATE_COMPILE)) { |
| ficlDictionary *locals; |
| |
| locals = ficlSystemGetLocals(vm->callback.system); |
| word = ficlDictionaryLookup(locals, name); |
| if (!word) |
| goto TO_GLOBAL; |
| |
| if (word->code == ficlPrimitiveDoLocalIm) { |
| instruction = ficlInstructionToLocalParen; |
| isDouble = isFloat = FICL_FALSE; |
| } else if (word->code == ficlPrimitiveDo2LocalIm) { |
| instruction = ficlInstructionTo2LocalParen; |
| isDouble = FICL_TRUE; |
| isFloat = FICL_FALSE; |
| } |
| #if FICL_WANT_FLOAT |
| else if (word->code == ficlPrimitiveDoFLocalIm) { |
| instruction = ficlInstructionToFLocalParen; |
| isDouble = FICL_FALSE; |
| isFloat = FICL_TRUE; |
| } else if (word->code == ficlPrimitiveDoF2LocalIm) { |
| instruction = ficlInstructionToF2LocalParen; |
| isDouble = isFloat = FICL_TRUE; |
| } |
| #endif /* FICL_WANT_FLOAT */ |
| else { |
| ficlVmThrowError(vm, |
| "to %.*s : local is of unknown type", |
| FICL_STRING_GET_LENGTH(name), |
| FICL_STRING_GET_POINTER(name)); |
| return; |
| } |
| |
| nLocal = word->param[0].i; |
| appendLocalOffset = FICL_TRUE; |
| |
| #if FICL_WANT_FLOAT |
| if (!isFloat) { |
| #endif /* FICL_WANT_FLOAT */ |
| if (nLocal == 0) { |
| instruction = |
| (isDouble) ? ficlInstructionTo2Local0 : |
| ficlInstructionToLocal0; |
| appendLocalOffset = FICL_FALSE; |
| } else if ((nLocal == 1) && !isDouble) { |
| instruction = ficlInstructionToLocal1; |
| appendLocalOffset = FICL_FALSE; |
| } |
| #if FICL_WANT_FLOAT |
| } |
| #endif /* FICL_WANT_FLOAT */ |
| |
| ficlDictionaryAppendUnsigned(dictionary, instruction); |
| if (appendLocalOffset) |
| ficlDictionaryAppendUnsigned(dictionary, nLocal); |
| return; |
| } |
| #endif |
| |
| #if FICL_WANT_LOCALS |
| TO_GLOBAL: |
| #endif /* FICL_WANT_LOCALS */ |
| word = ficlDictionaryLookup(dictionary, name); |
| if (!word) |
| ficlVmThrowError(vm, "%.*s not found", |
| FICL_STRING_GET_LENGTH(name), |
| FICL_STRING_GET_POINTER(name)); |
| |
| switch ((ficlInstruction)word->code) { |
| case ficlInstructionConstantParen: |
| instruction = ficlInstructionStore; |
| stack = vm->dataStack; |
| isDouble = FICL_FALSE; |
| break; |
| case ficlInstruction2ConstantParen: |
| instruction = ficlInstruction2Store; |
| stack = vm->dataStack; |
| isDouble = FICL_TRUE; |
| break; |
| #if FICL_WANT_FLOAT |
| case ficlInstructionFConstantParen: |
| instruction = ficlInstructionFStore; |
| stack = vm->floatStack; |
| isDouble = FICL_FALSE; |
| break; |
| case ficlInstructionF2ConstantParen: |
| instruction = ficlInstructionF2Store; |
| stack = vm->floatStack; |
| isDouble = FICL_TRUE; |
| break; |
| #endif /* FICL_WANT_FLOAT */ |
| default: |
| ficlVmThrowError(vm, |
| "to %.*s : value/constant is of unknown type", |
| FICL_STRING_GET_LENGTH(name), |
| FICL_STRING_GET_POINTER(name)); |
| return; |
| } |
| |
| if (vm->state == FICL_VM_STATE_INTERPRET) { |
| word->param[0] = ficlStackPop(stack); |
| if (isDouble) |
| word->param[1] = ficlStackPop(stack); |
| } else { |
| /* FICL_VM_STATE_COMPILE code to store to word's param */ |
| ficlStackPushPointer(vm->dataStack, &word->param[0]); |
| ficlPrimitiveLiteralIm(vm); |
| ficlDictionaryAppendUnsigned(dictionary, instruction); |
| } |
| } |
| |
| /* |
| * f m S l a s h M o d |
| * f-m-slash-mod CORE ( d1 n1 -- n2 n3 ) |
| * Divide d1 by n1, giving the floored quotient n3 and the remainder n2. |
| * Input and output stack arguments are signed. An ambiguous condition |
| * exists if n1 is zero or if the quotient lies outside the range of a |
| * single-ficlCell signed integer. |
| */ |
| static void |
| ficlPrimitiveFMSlashMod(ficlVm *vm) |
| { |
| ficl2Integer d1; |
| ficlInteger n1; |
| ficl2IntegerQR qr; |
| |
| FICL_STACK_CHECK(vm->dataStack, 3, 2); |
| |
| n1 = ficlStackPopInteger(vm->dataStack); |
| d1 = ficlStackPop2Integer(vm->dataStack); |
| qr = ficl2IntegerDivideFloored(d1, n1); |
| ficlStackPushInteger(vm->dataStack, qr.remainder); |
| ficlStackPushInteger(vm->dataStack, |
| FICL_2UNSIGNED_GET_LOW(qr.quotient)); |
| } |
| |
| /* |
| * s m S l a s h R e m |
| * s-m-slash-remainder CORE ( d1 n1 -- n2 n3 ) |
| * Divide d1 by n1, giving the symmetric quotient n3 and the remainder n2. |
| * Input and output stack arguments are signed. An ambiguous condition |
| * exists if n1 is zero or if the quotient lies outside the range of a |
| * single-ficlCell signed integer. |
| */ |
| static void |
| ficlPrimitiveSMSlashRem(ficlVm *vm) |
| { |
| ficl2Integer d1; |
| ficlInteger n1; |
| ficl2IntegerQR qr; |
| |
| FICL_STACK_CHECK(vm->dataStack, 3, 2); |
| |
| n1 = ficlStackPopInteger(vm->dataStack); |
| d1 = ficlStackPop2Integer(vm->dataStack); |
| qr = ficl2IntegerDivideSymmetric(d1, n1); |
| ficlStackPushInteger(vm->dataStack, qr.remainder); |
| ficlStackPushInteger(vm->dataStack, |
| FICL_2UNSIGNED_GET_LOW(qr.quotient)); |
| } |
| |
| static void |
| ficlPrimitiveMod(ficlVm *vm) |
| { |
| ficl2Integer d1; |
| ficlInteger n1; |
| ficlInteger i; |
| ficl2IntegerQR qr; |
| FICL_STACK_CHECK(vm->dataStack, 2, 1); |
| |
| n1 = ficlStackPopInteger(vm->dataStack); |
| i = ficlStackPopInteger(vm->dataStack); |
| FICL_INTEGER_TO_2INTEGER(i, d1); |
| qr = ficl2IntegerDivideSymmetric(d1, n1); |
| ficlStackPushInteger(vm->dataStack, qr.remainder); |
| } |
| |
| /* |
| * u m S l a s h M o d |
| * u-m-slash-mod CORE ( ud u1 -- u2 u3 ) |
| * Divide ud by u1, giving the quotient u3 and the remainder u2. |
| * All values and arithmetic are unsigned. An ambiguous condition |
| * exists if u1 is zero or if the quotient lies outside the range of a |
| * single-ficlCell unsigned integer. |
| */ |
| static void |
| ficlPrimitiveUMSlashMod(ficlVm *vm) |
| { |
| ficl2Unsigned ud; |
| ficlUnsigned u1; |
| ficl2UnsignedQR uqr; |
| |
| u1 = ficlStackPopUnsigned(vm->dataStack); |
| ud = ficlStackPop2Unsigned(vm->dataStack); |
| uqr = ficl2UnsignedDivide(ud, u1); |
| ficlStackPushUnsigned(vm->dataStack, uqr.remainder); |
| ficlStackPushUnsigned(vm->dataStack, |
| FICL_2UNSIGNED_GET_LOW(uqr.quotient)); |
| } |
| |
| /* |
| * m S t a r |
| * m-star CORE ( n1 n2 -- d ) |
| * d is the signed product of n1 times n2. |
| */ |
| static void |
| ficlPrimitiveMStar(ficlVm *vm) |
| { |
| ficlInteger n2; |
| ficlInteger n1; |
| ficl2Integer d; |
| FICL_STACK_CHECK(vm->dataStack, 2, 2); |
| |
| n2 = ficlStackPopInteger(vm->dataStack); |
| n1 = ficlStackPopInteger(vm->dataStack); |
| |
| d = ficl2IntegerMultiply(n1, n2); |
| ficlStackPush2Integer(vm->dataStack, d); |
| } |
| |
| static void |
| ficlPrimitiveUMStar(ficlVm *vm) |
| { |
| ficlUnsigned u2; |
| ficlUnsigned u1; |
| ficl2Unsigned ud; |
| FICL_STACK_CHECK(vm->dataStack, 2, 2); |
| |
| u2 = ficlStackPopUnsigned(vm->dataStack); |
| u1 = ficlStackPopUnsigned(vm->dataStack); |
| |
| ud = ficl2UnsignedMultiply(u1, u2); |
| ficlStackPush2Unsigned(vm->dataStack, ud); |
| } |
| |
| /* |
| * 2 r o t |
| * DOUBLE ( d1 d2 d3 -- d2 d3 d1 ) |
| */ |
| static void |
| ficlPrimitive2Rot(ficlVm *vm) |
| { |
| ficl2Integer d1, d2, d3; |
| FICL_STACK_CHECK(vm->dataStack, 6, 6); |
| |
| d3 = ficlStackPop2Integer(vm->dataStack); |
| d2 = ficlStackPop2Integer(vm->dataStack); |
| d1 = ficlStackPop2Integer(vm->dataStack); |
| ficlStackPush2Integer(vm->dataStack, d2); |
| ficlStackPush2Integer(vm->dataStack, d3); |
| ficlStackPush2Integer(vm->dataStack, d1); |
| } |
| |
| /* |
| * p a d |
| * CORE EXT ( -- c-addr ) |
| * c-addr is the address of a transient region that can be used to hold |
| * data for intermediate processing. |
| */ |
| static void |
| ficlPrimitivePad(ficlVm *vm) |
| { |
| ficlStackPushPointer(vm->dataStack, vm->pad); |
| } |
| |
| /* |
| * s o u r c e - i d |
| * CORE EXT, FILE ( -- 0 | -1 | fileid ) |
| * Identifies the input source as follows: |
| * |
| * SOURCE-ID Input source |
| * --------- ------------ |
| * fileid Text file fileid |
| * -1 String (via EVALUATE) |
| * 0 User input device |
| */ |
| static void |
| ficlPrimitiveSourceID(ficlVm *vm) |
| { |
| ficlStackPushInteger(vm->dataStack, vm->sourceId.i); |
| } |
| |
| /* |
| * r e f i l l |
| * CORE EXT ( -- flag ) |
| * Attempt to fill the input buffer from the input source, returning |
| * a FICL_TRUE flag if successful. |
| * When the input source is the user input device, attempt to receive input |
| * into the terminal input buffer. If successful, make the result the input |
| * buffer, set >IN to zero, and return FICL_TRUE. Receipt of a line containing |
| * no characters is considered successful. If there is no input available from |
| * the current input source, return FICL_FALSE. |
| * When the input source is a string from EVALUATE, return FICL_FALSE and |
| * perform no other action. |
| */ |
| static void |
| ficlPrimitiveRefill(ficlVm *vm) |
| { |
| ficlInteger ret = (vm->sourceId.i == -1) ? FICL_FALSE : FICL_TRUE; |
| if (ret && (vm->restart == 0)) |
| ficlVmThrow(vm, FICL_VM_STATUS_RESTART); |
| |
| ficlStackPushInteger(vm->dataStack, ret); |
| } |
| |
| /* |
| * freebsd exception handling words |
| * Catch, from ANS Forth standard. Installs a safety net, then EXECUTE |
| * the word in ToS. If an exception happens, restore the state to what |
| * it was before, and pushes the exception value on the stack. If not, |
| * push zero. |
| * |
| * Notice that Catch implements an inner interpreter. This is ugly, |
| * but given how Ficl works, it cannot be helped. The problem is that |
| * colon definitions will be executed *after* the function returns, |
| * while "code" definitions will be executed immediately. I considered |
| * other solutions to this problem, but all of them shared the same |
| * basic problem (with added disadvantages): if Ficl ever changes it's |
| * inner thread modus operandi, one would have to fix this word. |
| * |
| * More comments can be found throughout catch's code. |
| * |
| * Daniel C. Sobral Jan 09/1999 |
| * sadler may 2000 -- revised to follow ficl.c:ficlExecXT. |
| */ |
| static void |
| ficlPrimitiveCatch(ficlVm *vm) |
| { |
| int except; |
| jmp_buf vmState; |
| ficlVm vmCopy; |
| ficlStack dataStackCopy; |
| ficlStack returnStackCopy; |
| ficlWord *word; |
| |
| FICL_VM_ASSERT(vm, vm); |
| FICL_VM_ASSERT(vm, vm->callback.system->exitInnerWord); |
| |
| /* |
| * Get xt. |
| * We need this *before* we save the stack pointer, or |
| * we'll have to pop one element out of the stack after |
| * an exception. I prefer to get done with it up front. :-) |
| */ |
| |
| FICL_STACK_CHECK(vm->dataStack, 1, 0); |
| |
| word = ficlStackPopPointer(vm->dataStack); |
| |
| /* |
| * Save vm's state -- a catch will not back out environmental |
| * changes. |
| * |
| * We are *not* saving dictionary state, since it is |
| * global instead of per vm, and we are not saving |
| * stack contents, since we are not required to (and, |
| * thus, it would be useless). We save vm, and vm |
| * "stacks" (a structure containing general information |
| * about it, including the current stack pointer). |
| */ |
| memcpy((void*)&vmCopy, (void*)vm, sizeof (ficlVm)); |
| memcpy((void*)&dataStackCopy, (void*)vm->dataStack, sizeof (ficlStack)); |
| memcpy((void*)&returnStackCopy, (void*)vm->returnStack, |
| sizeof (ficlStack)); |
| |
| /* |
| * Give vm a jmp_buf |
| */ |
| vm->exceptionHandler = &vmState; |
| |
| /* |
| * Safety net |
| */ |
| except = setjmp(vmState); |
| |
| switch (except) { |
| /* |
| * Setup condition - push poison pill so that the VM throws |
| * VM_INNEREXIT if the XT terminates normally, then execute |
| * the XT |
| */ |
| case 0: |
| /* Open mouth, insert emetic */ |
| ficlVmPushIP(vm, &(vm->callback.system->exitInnerWord)); |
| ficlVmExecuteWord(vm, word); |
| ficlVmInnerLoop(vm, 0); |
| break; |
| |
| /* |
| * Normal exit from XT - lose the poison pill, |
| * restore old setjmp vector and push a zero. |
| */ |
| case FICL_VM_STATUS_INNER_EXIT: |
| ficlVmPopIP(vm); /* Gack - hurl poison pill */ |
| /* Restore just the setjmp vector */ |
| vm->exceptionHandler = vmCopy.exceptionHandler; |
| /* Push 0 -- everything is ok */ |
| ficlStackPushInteger(vm->dataStack, 0); |
| break; |
| |
| /* |
| * Some other exception got thrown - restore pre-existing VM state |
| * and push the exception code |
| */ |
| default: |
| /* Restore vm's state */ |
| memcpy((void*)vm, (void*)&vmCopy, sizeof (ficlVm)); |
| memcpy((void*)vm->dataStack, (void*)&dataStackCopy, |
| sizeof (ficlStack)); |
| memcpy((void*)vm->returnStack, (void*)&returnStackCopy, |
| sizeof (ficlStack)); |
| |
| ficlStackPushInteger(vm->dataStack, except); /* Push error */ |
| break; |
| } |
| } |
| |
| /* |
| * t h r o w |
| * EXCEPTION |
| * Throw -- From ANS Forth standard. |
| * |
| * Throw takes the ToS and, if that's different from zero, |
| * returns to the last executed catch context. Further throws will |
| * unstack previously executed "catches", in LIFO mode. |
| * |
| * Daniel C. Sobral Jan 09/1999 |
| */ |
| static void |
| ficlPrimitiveThrow(ficlVm *vm) |
| { |
| int except; |
| |
| except = ficlStackPopInteger(vm->dataStack); |
| |
| if (except) |
| ficlVmThrow(vm, except); |
| } |
| |
| /* |
| * a l l o c a t e |
| * MEMORY |
| */ |
| static void |
| ficlPrimitiveAllocate(ficlVm *vm) |
| { |
| size_t size; |
| void *p; |
| |
| size = ficlStackPopInteger(vm->dataStack); |
| p = ficlMalloc(size); |
| ficlStackPushPointer(vm->dataStack, p); |
| if (p != NULL) |
| ficlStackPushInteger(vm->dataStack, 0); |
| else |
| ficlStackPushInteger(vm->dataStack, 1); |
| } |
| |
| /* |
| * f r e e |
| * MEMORY |
| */ |
| static void |
| ficlPrimitiveFree(ficlVm *vm) |
| { |
| void *p; |
| |
| p = ficlStackPopPointer(vm->dataStack); |
| ficlFree(p); |
| ficlStackPushInteger(vm->dataStack, 0); |
| } |
| |
| |