1*ca987d46SWarner Losh /*******************************************************************
2*ca987d46SWarner Losh ** f i c l . c
3*ca987d46SWarner Losh ** Forth Inspired Command Language - external interface
4*ca987d46SWarner Losh ** Author: John Sadler (john_sadler@alum.mit.edu)
5*ca987d46SWarner Losh ** Created: 19 July 1997
6*ca987d46SWarner Losh ** $Id: ficl.c,v 1.16 2001/12/05 07:21:34 jsadler Exp $
7*ca987d46SWarner Losh *******************************************************************/
8*ca987d46SWarner Losh /*
9*ca987d46SWarner Losh ** This is an ANS Forth interpreter written in C.
10*ca987d46SWarner Losh ** Ficl uses Forth syntax for its commands, but turns the Forth
11*ca987d46SWarner Losh ** model on its head in other respects.
12*ca987d46SWarner Losh ** Ficl provides facilities for interoperating
13*ca987d46SWarner Losh ** with programs written in C: C functions can be exported to Ficl,
14*ca987d46SWarner Losh ** and Ficl commands can be executed via a C calling interface. The
15*ca987d46SWarner Losh ** interpreter is re-entrant, so it can be used in multiple instances
16*ca987d46SWarner Losh ** in a multitasking system. Unlike Forth, Ficl's outer interpreter
17*ca987d46SWarner Losh ** expects a text block as input, and returns to the caller after each
18*ca987d46SWarner Losh ** text block, so the data pump is somewhere in external code in the
19*ca987d46SWarner Losh ** style of TCL.
20*ca987d46SWarner Losh **
21*ca987d46SWarner Losh ** Code is written in ANSI C for portability.
22*ca987d46SWarner Losh */
23*ca987d46SWarner Losh /*
24*ca987d46SWarner Losh ** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
25*ca987d46SWarner Losh ** All rights reserved.
26*ca987d46SWarner Losh **
27*ca987d46SWarner Losh ** Get the latest Ficl release at http://ficl.sourceforge.net
28*ca987d46SWarner Losh **
29*ca987d46SWarner Losh ** I am interested in hearing from anyone who uses ficl. If you have
30*ca987d46SWarner Losh ** a problem, a success story, a defect, an enhancement request, or
31*ca987d46SWarner Losh ** if you would like to contribute to the ficl release, please
32*ca987d46SWarner Losh ** contact me by email at the address above.
33*ca987d46SWarner Losh **
34*ca987d46SWarner Losh ** L I C E N S E and D I S C L A I M E R
35*ca987d46SWarner Losh **
36*ca987d46SWarner Losh ** Redistribution and use in source and binary forms, with or without
37*ca987d46SWarner Losh ** modification, are permitted provided that the following conditions
38*ca987d46SWarner Losh ** are met:
39*ca987d46SWarner Losh ** 1. Redistributions of source code must retain the above copyright
40*ca987d46SWarner Losh ** notice, this list of conditions and the following disclaimer.
41*ca987d46SWarner Losh ** 2. Redistributions in binary form must reproduce the above copyright
42*ca987d46SWarner Losh ** notice, this list of conditions and the following disclaimer in the
43*ca987d46SWarner Losh ** documentation and/or other materials provided with the distribution.
44*ca987d46SWarner Losh **
45*ca987d46SWarner Losh ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46*ca987d46SWarner Losh ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47*ca987d46SWarner Losh ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48*ca987d46SWarner Losh ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49*ca987d46SWarner Losh ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50*ca987d46SWarner Losh ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51*ca987d46SWarner Losh ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52*ca987d46SWarner Losh ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53*ca987d46SWarner Losh ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54*ca987d46SWarner Losh ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55*ca987d46SWarner Losh ** SUCH DAMAGE.
56*ca987d46SWarner Losh */
57*ca987d46SWarner Losh
58*ca987d46SWarner Losh
59*ca987d46SWarner Losh #ifdef TESTMAIN
60*ca987d46SWarner Losh #include <stdlib.h>
61*ca987d46SWarner Losh #else
62*ca987d46SWarner Losh #include <stand.h>
63*ca987d46SWarner Losh #endif
64*ca987d46SWarner Losh #include <string.h>
65*ca987d46SWarner Losh #include "ficl.h"
66*ca987d46SWarner Losh
67*ca987d46SWarner Losh
68*ca987d46SWarner Losh /*
69*ca987d46SWarner Losh ** System statics
70*ca987d46SWarner Losh ** Each FICL_SYSTEM builds a global dictionary during its start
71*ca987d46SWarner Losh ** sequence. This is shared by all virtual machines of that system.
72*ca987d46SWarner Losh ** Therefore only one VM can update the dictionary
73*ca987d46SWarner Losh ** at a time. The system imports a locking function that
74*ca987d46SWarner Losh ** you can override in order to control update access to
75*ca987d46SWarner Losh ** the dictionary. The function is stubbed out by default,
76*ca987d46SWarner Losh ** but you can insert one: #define FICL_MULTITHREAD 1
77*ca987d46SWarner Losh ** and supply your own version of ficlLockDictionary.
78*ca987d46SWarner Losh */
79*ca987d46SWarner Losh static int defaultStack = FICL_DEFAULT_STACK;
80*ca987d46SWarner Losh
81*ca987d46SWarner Losh
82*ca987d46SWarner Losh static void ficlSetVersionEnv(FICL_SYSTEM *pSys);
83*ca987d46SWarner Losh
84*ca987d46SWarner Losh
85*ca987d46SWarner Losh /**************************************************************************
86*ca987d46SWarner Losh f i c l I n i t S y s t e m
87*ca987d46SWarner Losh ** Binds a global dictionary to the interpreter system.
88*ca987d46SWarner Losh ** You specify the address and size of the allocated area.
89*ca987d46SWarner Losh ** After that, ficl manages it.
90*ca987d46SWarner Losh ** First step is to set up the static pointers to the area.
91*ca987d46SWarner Losh ** Then write the "precompiled" portion of the dictionary in.
92*ca987d46SWarner Losh ** The dictionary needs to be at least large enough to hold the
93*ca987d46SWarner Losh ** precompiled part. Try 1K cells minimum. Use "words" to find
94*ca987d46SWarner Losh ** out how much of the dictionary is used at any time.
95*ca987d46SWarner Losh **************************************************************************/
ficlInitSystemEx(FICL_SYSTEM_INFO * fsi)96*ca987d46SWarner Losh FICL_SYSTEM *ficlInitSystemEx(FICL_SYSTEM_INFO *fsi)
97*ca987d46SWarner Losh {
98*ca987d46SWarner Losh int nDictCells;
99*ca987d46SWarner Losh int nEnvCells;
100*ca987d46SWarner Losh FICL_SYSTEM *pSys = ficlMalloc(sizeof (FICL_SYSTEM));
101*ca987d46SWarner Losh
102*ca987d46SWarner Losh assert(pSys);
103*ca987d46SWarner Losh assert(fsi->size == sizeof (FICL_SYSTEM_INFO));
104*ca987d46SWarner Losh
105*ca987d46SWarner Losh memset(pSys, 0, sizeof (FICL_SYSTEM));
106*ca987d46SWarner Losh
107*ca987d46SWarner Losh nDictCells = fsi->nDictCells;
108*ca987d46SWarner Losh if (nDictCells <= 0)
109*ca987d46SWarner Losh nDictCells = FICL_DEFAULT_DICT;
110*ca987d46SWarner Losh
111*ca987d46SWarner Losh nEnvCells = fsi->nEnvCells;
112*ca987d46SWarner Losh if (nEnvCells <= 0)
113*ca987d46SWarner Losh nEnvCells = FICL_DEFAULT_DICT;
114*ca987d46SWarner Losh
115*ca987d46SWarner Losh pSys->dp = dictCreateHashed((unsigned)nDictCells, HASHSIZE);
116*ca987d46SWarner Losh pSys->dp->pForthWords->name = "forth-wordlist";
117*ca987d46SWarner Losh
118*ca987d46SWarner Losh pSys->envp = dictCreate((unsigned)nEnvCells);
119*ca987d46SWarner Losh pSys->envp->pForthWords->name = "environment";
120*ca987d46SWarner Losh
121*ca987d46SWarner Losh pSys->textOut = fsi->textOut;
122*ca987d46SWarner Losh pSys->pExtend = fsi->pExtend;
123*ca987d46SWarner Losh
124*ca987d46SWarner Losh #if FICL_WANT_LOCALS
125*ca987d46SWarner Losh /*
126*ca987d46SWarner Losh ** The locals dictionary is only searched while compiling,
127*ca987d46SWarner Losh ** but this is where speed is most important. On the other
128*ca987d46SWarner Losh ** hand, the dictionary gets emptied after each use of locals
129*ca987d46SWarner Losh ** The need to balance search speed with the cost of the 'empty'
130*ca987d46SWarner Losh ** operation led me to select a single-threaded list...
131*ca987d46SWarner Losh */
132*ca987d46SWarner Losh pSys->localp = dictCreate((unsigned)FICL_MAX_LOCALS * CELLS_PER_WORD);
133*ca987d46SWarner Losh #endif
134*ca987d46SWarner Losh
135*ca987d46SWarner Losh /*
136*ca987d46SWarner Losh ** Build the precompiled dictionary and load softwords. We need a temporary
137*ca987d46SWarner Losh ** VM to do this - ficlNewVM links one to the head of the system VM list.
138*ca987d46SWarner Losh ** ficlCompilePlatform (defined in win32.c, for example) adds platform specific words.
139*ca987d46SWarner Losh */
140*ca987d46SWarner Losh ficlCompileCore(pSys);
141*ca987d46SWarner Losh ficlCompilePrefix(pSys);
142*ca987d46SWarner Losh #if FICL_WANT_FLOAT
143*ca987d46SWarner Losh ficlCompileFloat(pSys);
144*ca987d46SWarner Losh #endif
145*ca987d46SWarner Losh #if FICL_PLATFORM_EXTEND
146*ca987d46SWarner Losh ficlCompilePlatform(pSys);
147*ca987d46SWarner Losh #endif
148*ca987d46SWarner Losh ficlSetVersionEnv(pSys);
149*ca987d46SWarner Losh
150*ca987d46SWarner Losh /*
151*ca987d46SWarner Losh ** Establish the parse order. Note that prefixes precede numbers -
152*ca987d46SWarner Losh ** this allows constructs like "0b101010" which might parse as a
153*ca987d46SWarner Losh ** hex value otherwise.
154*ca987d46SWarner Losh */
155*ca987d46SWarner Losh ficlAddPrecompiledParseStep(pSys, "?prefix", ficlParsePrefix);
156*ca987d46SWarner Losh ficlAddPrecompiledParseStep(pSys, "?number", ficlParseNumber);
157*ca987d46SWarner Losh #if FICL_WANT_FLOAT
158*ca987d46SWarner Losh ficlAddPrecompiledParseStep(pSys, ">float", ficlParseFloatNumber);
159*ca987d46SWarner Losh #endif
160*ca987d46SWarner Losh
161*ca987d46SWarner Losh /*
162*ca987d46SWarner Losh ** Now create a temporary VM to compile the softwords. Since all VMs are
163*ca987d46SWarner Losh ** linked into the vmList of FICL_SYSTEM, we don't have to pass the VM
164*ca987d46SWarner Losh ** to ficlCompileSoftCore -- it just hijacks whatever it finds in the VM list.
165*ca987d46SWarner Losh ** ficl 2.05: vmCreate no longer depends on the presence of INTERPRET in the
166*ca987d46SWarner Losh ** dictionary, so a VM can be created before the dictionary is built. It just
167*ca987d46SWarner Losh ** can't do much...
168*ca987d46SWarner Losh */
169*ca987d46SWarner Losh ficlNewVM(pSys);
170*ca987d46SWarner Losh ficlCompileSoftCore(pSys);
171*ca987d46SWarner Losh ficlFreeVM(pSys->vmList);
172*ca987d46SWarner Losh
173*ca987d46SWarner Losh
174*ca987d46SWarner Losh return pSys;
175*ca987d46SWarner Losh }
176*ca987d46SWarner Losh
177*ca987d46SWarner Losh
ficlInitSystem(int nDictCells)178*ca987d46SWarner Losh FICL_SYSTEM *ficlInitSystem(int nDictCells)
179*ca987d46SWarner Losh {
180*ca987d46SWarner Losh FICL_SYSTEM_INFO fsi;
181*ca987d46SWarner Losh ficlInitInfo(&fsi);
182*ca987d46SWarner Losh fsi.nDictCells = nDictCells;
183*ca987d46SWarner Losh return ficlInitSystemEx(&fsi);
184*ca987d46SWarner Losh }
185*ca987d46SWarner Losh
186*ca987d46SWarner Losh
187*ca987d46SWarner Losh /**************************************************************************
188*ca987d46SWarner Losh f i c l A d d P a r s e S t e p
189*ca987d46SWarner Losh ** Appends a parse step function to the end of the parse list (see
190*ca987d46SWarner Losh ** FICL_PARSE_STEP notes in ficl.h for details). Returns 0 if successful,
191*ca987d46SWarner Losh ** nonzero if there's no more room in the list.
192*ca987d46SWarner Losh **************************************************************************/
ficlAddParseStep(FICL_SYSTEM * pSys,FICL_WORD * pFW)193*ca987d46SWarner Losh int ficlAddParseStep(FICL_SYSTEM *pSys, FICL_WORD *pFW)
194*ca987d46SWarner Losh {
195*ca987d46SWarner Losh int i;
196*ca987d46SWarner Losh for (i = 0; i < FICL_MAX_PARSE_STEPS; i++)
197*ca987d46SWarner Losh {
198*ca987d46SWarner Losh if (pSys->parseList[i] == NULL)
199*ca987d46SWarner Losh {
200*ca987d46SWarner Losh pSys->parseList[i] = pFW;
201*ca987d46SWarner Losh return 0;
202*ca987d46SWarner Losh }
203*ca987d46SWarner Losh }
204*ca987d46SWarner Losh
205*ca987d46SWarner Losh return 1;
206*ca987d46SWarner Losh }
207*ca987d46SWarner Losh
208*ca987d46SWarner Losh
209*ca987d46SWarner Losh /*
210*ca987d46SWarner Losh ** Compile a word into the dictionary that invokes the specified FICL_PARSE_STEP
211*ca987d46SWarner Losh ** function. It is up to the user (as usual in Forth) to make sure the stack
212*ca987d46SWarner Losh ** preconditions are valid (there needs to be a counted string on top of the stack)
213*ca987d46SWarner Losh ** before using the resulting word.
214*ca987d46SWarner Losh */
ficlAddPrecompiledParseStep(FICL_SYSTEM * pSys,char * name,FICL_PARSE_STEP pStep)215*ca987d46SWarner Losh void ficlAddPrecompiledParseStep(FICL_SYSTEM *pSys, char *name, FICL_PARSE_STEP pStep)
216*ca987d46SWarner Losh {
217*ca987d46SWarner Losh FICL_DICT *dp = pSys->dp;
218*ca987d46SWarner Losh FICL_WORD *pFW = dictAppendWord(dp, name, parseStepParen, FW_DEFAULT);
219*ca987d46SWarner Losh dictAppendCell(dp, LVALUEtoCELL(pStep));
220*ca987d46SWarner Losh ficlAddParseStep(pSys, pFW);
221*ca987d46SWarner Losh }
222*ca987d46SWarner Losh
223*ca987d46SWarner Losh
224*ca987d46SWarner Losh /*
225*ca987d46SWarner Losh ** This word lists the parse steps in order
226*ca987d46SWarner Losh */
ficlListParseSteps(FICL_VM * pVM)227*ca987d46SWarner Losh void ficlListParseSteps(FICL_VM *pVM)
228*ca987d46SWarner Losh {
229*ca987d46SWarner Losh int i;
230*ca987d46SWarner Losh FICL_SYSTEM *pSys = pVM->pSys;
231*ca987d46SWarner Losh assert(pSys);
232*ca987d46SWarner Losh
233*ca987d46SWarner Losh vmTextOut(pVM, "Parse steps:", 1);
234*ca987d46SWarner Losh vmTextOut(pVM, "lookup", 1);
235*ca987d46SWarner Losh
236*ca987d46SWarner Losh for (i = 0; i < FICL_MAX_PARSE_STEPS; i++)
237*ca987d46SWarner Losh {
238*ca987d46SWarner Losh if (pSys->parseList[i] != NULL)
239*ca987d46SWarner Losh {
240*ca987d46SWarner Losh vmTextOut(pVM, pSys->parseList[i]->name, 1);
241*ca987d46SWarner Losh }
242*ca987d46SWarner Losh else break;
243*ca987d46SWarner Losh }
244*ca987d46SWarner Losh return;
245*ca987d46SWarner Losh }
246*ca987d46SWarner Losh
247*ca987d46SWarner Losh
248*ca987d46SWarner Losh /**************************************************************************
249*ca987d46SWarner Losh f i c l N e w V M
250*ca987d46SWarner Losh ** Create a new virtual machine and link it into the system list
251*ca987d46SWarner Losh ** of VMs for later cleanup by ficlTermSystem.
252*ca987d46SWarner Losh **************************************************************************/
ficlNewVM(FICL_SYSTEM * pSys)253*ca987d46SWarner Losh FICL_VM *ficlNewVM(FICL_SYSTEM *pSys)
254*ca987d46SWarner Losh {
255*ca987d46SWarner Losh FICL_VM *pVM = vmCreate(NULL, defaultStack, defaultStack);
256*ca987d46SWarner Losh pVM->link = pSys->vmList;
257*ca987d46SWarner Losh pVM->pSys = pSys;
258*ca987d46SWarner Losh pVM->pExtend = pSys->pExtend;
259*ca987d46SWarner Losh vmSetTextOut(pVM, pSys->textOut);
260*ca987d46SWarner Losh
261*ca987d46SWarner Losh pSys->vmList = pVM;
262*ca987d46SWarner Losh return pVM;
263*ca987d46SWarner Losh }
264*ca987d46SWarner Losh
265*ca987d46SWarner Losh
266*ca987d46SWarner Losh /**************************************************************************
267*ca987d46SWarner Losh f i c l F r e e V M
268*ca987d46SWarner Losh ** Removes the VM in question from the system VM list and deletes the
269*ca987d46SWarner Losh ** memory allocated to it. This is an optional call, since ficlTermSystem
270*ca987d46SWarner Losh ** will do this cleanup for you. This function is handy if you're going to
271*ca987d46SWarner Losh ** do a lot of dynamic creation of VMs.
272*ca987d46SWarner Losh **************************************************************************/
ficlFreeVM(FICL_VM * pVM)273*ca987d46SWarner Losh void ficlFreeVM(FICL_VM *pVM)
274*ca987d46SWarner Losh {
275*ca987d46SWarner Losh FICL_SYSTEM *pSys = pVM->pSys;
276*ca987d46SWarner Losh FICL_VM *pList = pSys->vmList;
277*ca987d46SWarner Losh
278*ca987d46SWarner Losh assert(pVM != NULL);
279*ca987d46SWarner Losh
280*ca987d46SWarner Losh if (pSys->vmList == pVM)
281*ca987d46SWarner Losh {
282*ca987d46SWarner Losh pSys->vmList = pSys->vmList->link;
283*ca987d46SWarner Losh }
284*ca987d46SWarner Losh else for (; pList != NULL; pList = pList->link)
285*ca987d46SWarner Losh {
286*ca987d46SWarner Losh if (pList->link == pVM)
287*ca987d46SWarner Losh {
288*ca987d46SWarner Losh pList->link = pVM->link;
289*ca987d46SWarner Losh break;
290*ca987d46SWarner Losh }
291*ca987d46SWarner Losh }
292*ca987d46SWarner Losh
293*ca987d46SWarner Losh if (pList)
294*ca987d46SWarner Losh vmDelete(pVM);
295*ca987d46SWarner Losh return;
296*ca987d46SWarner Losh }
297*ca987d46SWarner Losh
298*ca987d46SWarner Losh
299*ca987d46SWarner Losh /**************************************************************************
300*ca987d46SWarner Losh f i c l B u i l d
301*ca987d46SWarner Losh ** Builds a word into the dictionary.
302*ca987d46SWarner Losh ** Preconditions: system must be initialized, and there must
303*ca987d46SWarner Losh ** be enough space for the new word's header! Operation is
304*ca987d46SWarner Losh ** controlled by ficlLockDictionary, so any initialization
305*ca987d46SWarner Losh ** required by your version of the function (if you overrode
306*ca987d46SWarner Losh ** it) must be complete at this point.
307*ca987d46SWarner Losh ** Parameters:
308*ca987d46SWarner Losh ** name -- duh, the name of the word
309*ca987d46SWarner Losh ** code -- code to execute when the word is invoked - must take a single param
310*ca987d46SWarner Losh ** pointer to a FICL_VM
311*ca987d46SWarner Losh ** flags -- 0 or more of F_IMMEDIATE, F_COMPILE, use bitwise OR!
312*ca987d46SWarner Losh **
313*ca987d46SWarner Losh **************************************************************************/
ficlBuild(FICL_SYSTEM * pSys,char * name,FICL_CODE code,char flags)314*ca987d46SWarner Losh int ficlBuild(FICL_SYSTEM *pSys, char *name, FICL_CODE code, char flags)
315*ca987d46SWarner Losh {
316*ca987d46SWarner Losh #if FICL_MULTITHREAD
317*ca987d46SWarner Losh int err = ficlLockDictionary(TRUE);
318*ca987d46SWarner Losh if (err) return err;
319*ca987d46SWarner Losh #endif /* FICL_MULTITHREAD */
320*ca987d46SWarner Losh
321*ca987d46SWarner Losh assert(dictCellsAvail(pSys->dp) > sizeof (FICL_WORD) / sizeof (CELL));
322*ca987d46SWarner Losh dictAppendWord(pSys->dp, name, code, flags);
323*ca987d46SWarner Losh
324*ca987d46SWarner Losh ficlLockDictionary(FALSE);
325*ca987d46SWarner Losh return 0;
326*ca987d46SWarner Losh }
327*ca987d46SWarner Losh
328*ca987d46SWarner Losh
329*ca987d46SWarner Losh /**************************************************************************
330*ca987d46SWarner Losh f i c l E v a l u a t e
331*ca987d46SWarner Losh ** Wrapper for ficlExec() which sets SOURCE-ID to -1.
332*ca987d46SWarner Losh **************************************************************************/
ficlEvaluate(FICL_VM * pVM,char * pText)333*ca987d46SWarner Losh int ficlEvaluate(FICL_VM *pVM, char *pText)
334*ca987d46SWarner Losh {
335*ca987d46SWarner Losh int returnValue;
336*ca987d46SWarner Losh CELL id = pVM->sourceID;
337*ca987d46SWarner Losh pVM->sourceID.i = -1;
338*ca987d46SWarner Losh returnValue = ficlExecC(pVM, pText, -1);
339*ca987d46SWarner Losh pVM->sourceID = id;
340*ca987d46SWarner Losh return returnValue;
341*ca987d46SWarner Losh }
342*ca987d46SWarner Losh
343*ca987d46SWarner Losh
344*ca987d46SWarner Losh /**************************************************************************
345*ca987d46SWarner Losh f i c l E x e c
346*ca987d46SWarner Losh ** Evaluates a block of input text in the context of the
347*ca987d46SWarner Losh ** specified interpreter. Emits any requested output to the
348*ca987d46SWarner Losh ** interpreter's output function.
349*ca987d46SWarner Losh **
350*ca987d46SWarner Losh ** Contains the "inner interpreter" code in a tight loop
351*ca987d46SWarner Losh **
352*ca987d46SWarner Losh ** Returns one of the VM_XXXX codes defined in ficl.h:
353*ca987d46SWarner Losh ** VM_OUTOFTEXT is the normal exit condition
354*ca987d46SWarner Losh ** VM_ERREXIT means that the interp encountered a syntax error
355*ca987d46SWarner Losh ** and the vm has been reset to recover (some or all
356*ca987d46SWarner Losh ** of the text block got ignored
357*ca987d46SWarner Losh ** VM_USEREXIT means that the user executed the "bye" command
358*ca987d46SWarner Losh ** to shut down the interpreter. This would be a good
359*ca987d46SWarner Losh ** time to delete the vm, etc -- or you can ignore this
360*ca987d46SWarner Losh ** signal.
361*ca987d46SWarner Losh **************************************************************************/
ficlExec(FICL_VM * pVM,char * pText)362*ca987d46SWarner Losh int ficlExec(FICL_VM *pVM, char *pText)
363*ca987d46SWarner Losh {
364*ca987d46SWarner Losh return ficlExecC(pVM, pText, -1);
365*ca987d46SWarner Losh }
366*ca987d46SWarner Losh
ficlExecC(FICL_VM * pVM,char * pText,FICL_INT size)367*ca987d46SWarner Losh int ficlExecC(FICL_VM *pVM, char *pText, FICL_INT size)
368*ca987d46SWarner Losh {
369*ca987d46SWarner Losh FICL_SYSTEM *pSys = pVM->pSys;
370*ca987d46SWarner Losh FICL_DICT *dp = pSys->dp;
371*ca987d46SWarner Losh
372*ca987d46SWarner Losh int except;
373*ca987d46SWarner Losh jmp_buf vmState;
374*ca987d46SWarner Losh jmp_buf *oldState;
375*ca987d46SWarner Losh TIB saveTib;
376*ca987d46SWarner Losh
377*ca987d46SWarner Losh assert(pVM);
378*ca987d46SWarner Losh assert(pSys->pInterp[0]);
379*ca987d46SWarner Losh
380*ca987d46SWarner Losh if (size < 0)
381*ca987d46SWarner Losh size = strlen(pText);
382*ca987d46SWarner Losh
383*ca987d46SWarner Losh vmPushTib(pVM, pText, size, &saveTib);
384*ca987d46SWarner Losh
385*ca987d46SWarner Losh /*
386*ca987d46SWarner Losh ** Save and restore VM's jmp_buf to enable nested calls to ficlExec
387*ca987d46SWarner Losh */
388*ca987d46SWarner Losh oldState = pVM->pState;
389*ca987d46SWarner Losh pVM->pState = &vmState; /* This has to come before the setjmp! */
390*ca987d46SWarner Losh except = setjmp(vmState);
391*ca987d46SWarner Losh
392*ca987d46SWarner Losh switch (except)
393*ca987d46SWarner Losh {
394*ca987d46SWarner Losh case 0:
395*ca987d46SWarner Losh if (pVM->fRestart)
396*ca987d46SWarner Losh {
397*ca987d46SWarner Losh pVM->runningWord->code(pVM);
398*ca987d46SWarner Losh pVM->fRestart = 0;
399*ca987d46SWarner Losh }
400*ca987d46SWarner Losh else
401*ca987d46SWarner Losh { /* set VM up to interpret text */
402*ca987d46SWarner Losh vmPushIP(pVM, &(pSys->pInterp[0]));
403*ca987d46SWarner Losh }
404*ca987d46SWarner Losh
405*ca987d46SWarner Losh vmInnerLoop(pVM);
406*ca987d46SWarner Losh break;
407*ca987d46SWarner Losh
408*ca987d46SWarner Losh case VM_RESTART:
409*ca987d46SWarner Losh pVM->fRestart = 1;
410*ca987d46SWarner Losh except = VM_OUTOFTEXT;
411*ca987d46SWarner Losh break;
412*ca987d46SWarner Losh
413*ca987d46SWarner Losh case VM_OUTOFTEXT:
414*ca987d46SWarner Losh vmPopIP(pVM);
415*ca987d46SWarner Losh #ifdef TESTMAIN
416*ca987d46SWarner Losh if ((pVM->state != COMPILE) && (pVM->sourceID.i == 0))
417*ca987d46SWarner Losh ficlTextOut(pVM, FICL_PROMPT, 0);
418*ca987d46SWarner Losh #endif
419*ca987d46SWarner Losh break;
420*ca987d46SWarner Losh
421*ca987d46SWarner Losh case VM_USEREXIT:
422*ca987d46SWarner Losh case VM_INNEREXIT:
423*ca987d46SWarner Losh case VM_BREAK:
424*ca987d46SWarner Losh break;
425*ca987d46SWarner Losh
426*ca987d46SWarner Losh case VM_QUIT:
427*ca987d46SWarner Losh if (pVM->state == COMPILE)
428*ca987d46SWarner Losh {
429*ca987d46SWarner Losh dictAbortDefinition(dp);
430*ca987d46SWarner Losh #if FICL_WANT_LOCALS
431*ca987d46SWarner Losh dictEmpty(pSys->localp, pSys->localp->pForthWords->size);
432*ca987d46SWarner Losh #endif
433*ca987d46SWarner Losh }
434*ca987d46SWarner Losh vmQuit(pVM);
435*ca987d46SWarner Losh break;
436*ca987d46SWarner Losh
437*ca987d46SWarner Losh case VM_ERREXIT:
438*ca987d46SWarner Losh case VM_ABORT:
439*ca987d46SWarner Losh case VM_ABORTQ:
440*ca987d46SWarner Losh default: /* user defined exit code?? */
441*ca987d46SWarner Losh if (pVM->state == COMPILE)
442*ca987d46SWarner Losh {
443*ca987d46SWarner Losh dictAbortDefinition(dp);
444*ca987d46SWarner Losh #if FICL_WANT_LOCALS
445*ca987d46SWarner Losh dictEmpty(pSys->localp, pSys->localp->pForthWords->size);
446*ca987d46SWarner Losh #endif
447*ca987d46SWarner Losh }
448*ca987d46SWarner Losh dictResetSearchOrder(dp);
449*ca987d46SWarner Losh vmReset(pVM);
450*ca987d46SWarner Losh break;
451*ca987d46SWarner Losh }
452*ca987d46SWarner Losh
453*ca987d46SWarner Losh pVM->pState = oldState;
454*ca987d46SWarner Losh vmPopTib(pVM, &saveTib);
455*ca987d46SWarner Losh return (except);
456*ca987d46SWarner Losh }
457*ca987d46SWarner Losh
458*ca987d46SWarner Losh
459*ca987d46SWarner Losh /**************************************************************************
460*ca987d46SWarner Losh f i c l E x e c X T
461*ca987d46SWarner Losh ** Given a pointer to a FICL_WORD, push an inner interpreter and
462*ca987d46SWarner Losh ** execute the word to completion. This is in contrast with vmExecute,
463*ca987d46SWarner Losh ** which does not guarantee that the word will have completed when
464*ca987d46SWarner Losh ** the function returns (ie in the case of colon definitions, which
465*ca987d46SWarner Losh ** need an inner interpreter to finish)
466*ca987d46SWarner Losh **
467*ca987d46SWarner Losh ** Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
468*ca987d46SWarner Losh ** exit condition is VM_INNEREXIT, ficl's private signal to exit the
469*ca987d46SWarner Losh ** inner loop under normal circumstances. If another code is thrown to
470*ca987d46SWarner Losh ** exit the loop, this function will re-throw it if it's nested under
471*ca987d46SWarner Losh ** itself or ficlExec.
472*ca987d46SWarner Losh **
473*ca987d46SWarner Losh ** NOTE: this function is intended so that C code can execute ficlWords
474*ca987d46SWarner Losh ** given their address in the dictionary (xt).
475*ca987d46SWarner Losh **************************************************************************/
ficlExecXT(FICL_VM * pVM,FICL_WORD * pWord)476*ca987d46SWarner Losh int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord)
477*ca987d46SWarner Losh {
478*ca987d46SWarner Losh int except;
479*ca987d46SWarner Losh jmp_buf vmState;
480*ca987d46SWarner Losh jmp_buf *oldState;
481*ca987d46SWarner Losh FICL_WORD *oldRunningWord;
482*ca987d46SWarner Losh
483*ca987d46SWarner Losh assert(pVM);
484*ca987d46SWarner Losh assert(pVM->pSys->pExitInner);
485*ca987d46SWarner Losh
486*ca987d46SWarner Losh /*
487*ca987d46SWarner Losh ** Save the runningword so that RESTART behaves correctly
488*ca987d46SWarner Losh ** over nested calls.
489*ca987d46SWarner Losh */
490*ca987d46SWarner Losh oldRunningWord = pVM->runningWord;
491*ca987d46SWarner Losh /*
492*ca987d46SWarner Losh ** Save and restore VM's jmp_buf to enable nested calls
493*ca987d46SWarner Losh */
494*ca987d46SWarner Losh oldState = pVM->pState;
495*ca987d46SWarner Losh pVM->pState = &vmState; /* This has to come before the setjmp! */
496*ca987d46SWarner Losh except = setjmp(vmState);
497*ca987d46SWarner Losh
498*ca987d46SWarner Losh if (except)
499*ca987d46SWarner Losh vmPopIP(pVM);
500*ca987d46SWarner Losh else
501*ca987d46SWarner Losh vmPushIP(pVM, &(pVM->pSys->pExitInner));
502*ca987d46SWarner Losh
503*ca987d46SWarner Losh switch (except)
504*ca987d46SWarner Losh {
505*ca987d46SWarner Losh case 0:
506*ca987d46SWarner Losh vmExecute(pVM, pWord);
507*ca987d46SWarner Losh vmInnerLoop(pVM);
508*ca987d46SWarner Losh break;
509*ca987d46SWarner Losh
510*ca987d46SWarner Losh case VM_INNEREXIT:
511*ca987d46SWarner Losh case VM_BREAK:
512*ca987d46SWarner Losh break;
513*ca987d46SWarner Losh
514*ca987d46SWarner Losh case VM_RESTART:
515*ca987d46SWarner Losh case VM_OUTOFTEXT:
516*ca987d46SWarner Losh case VM_USEREXIT:
517*ca987d46SWarner Losh case VM_QUIT:
518*ca987d46SWarner Losh case VM_ERREXIT:
519*ca987d46SWarner Losh case VM_ABORT:
520*ca987d46SWarner Losh case VM_ABORTQ:
521*ca987d46SWarner Losh default: /* user defined exit code?? */
522*ca987d46SWarner Losh if (oldState)
523*ca987d46SWarner Losh {
524*ca987d46SWarner Losh pVM->pState = oldState;
525*ca987d46SWarner Losh vmThrow(pVM, except);
526*ca987d46SWarner Losh }
527*ca987d46SWarner Losh break;
528*ca987d46SWarner Losh }
529*ca987d46SWarner Losh
530*ca987d46SWarner Losh pVM->pState = oldState;
531*ca987d46SWarner Losh pVM->runningWord = oldRunningWord;
532*ca987d46SWarner Losh return (except);
533*ca987d46SWarner Losh }
534*ca987d46SWarner Losh
535*ca987d46SWarner Losh
536*ca987d46SWarner Losh /**************************************************************************
537*ca987d46SWarner Losh f i c l L o o k u p
538*ca987d46SWarner Losh ** Look in the system dictionary for a match to the given name. If
539*ca987d46SWarner Losh ** found, return the address of the corresponding FICL_WORD. Otherwise
540*ca987d46SWarner Losh ** return NULL.
541*ca987d46SWarner Losh **************************************************************************/
ficlLookup(FICL_SYSTEM * pSys,char * name)542*ca987d46SWarner Losh FICL_WORD *ficlLookup(FICL_SYSTEM *pSys, char *name)
543*ca987d46SWarner Losh {
544*ca987d46SWarner Losh STRINGINFO si;
545*ca987d46SWarner Losh SI_PSZ(si, name);
546*ca987d46SWarner Losh return dictLookup(pSys->dp, si);
547*ca987d46SWarner Losh }
548*ca987d46SWarner Losh
549*ca987d46SWarner Losh
550*ca987d46SWarner Losh /**************************************************************************
551*ca987d46SWarner Losh f i c l G e t D i c t
552*ca987d46SWarner Losh ** Returns the address of the system dictionary
553*ca987d46SWarner Losh **************************************************************************/
ficlGetDict(FICL_SYSTEM * pSys)554*ca987d46SWarner Losh FICL_DICT *ficlGetDict(FICL_SYSTEM *pSys)
555*ca987d46SWarner Losh {
556*ca987d46SWarner Losh return pSys->dp;
557*ca987d46SWarner Losh }
558*ca987d46SWarner Losh
559*ca987d46SWarner Losh
560*ca987d46SWarner Losh /**************************************************************************
561*ca987d46SWarner Losh f i c l G e t E n v
562*ca987d46SWarner Losh ** Returns the address of the system environment space
563*ca987d46SWarner Losh **************************************************************************/
ficlGetEnv(FICL_SYSTEM * pSys)564*ca987d46SWarner Losh FICL_DICT *ficlGetEnv(FICL_SYSTEM *pSys)
565*ca987d46SWarner Losh {
566*ca987d46SWarner Losh return pSys->envp;
567*ca987d46SWarner Losh }
568*ca987d46SWarner Losh
569*ca987d46SWarner Losh
570*ca987d46SWarner Losh /**************************************************************************
571*ca987d46SWarner Losh f i c l S e t E n v
572*ca987d46SWarner Losh ** Create an environment variable with a one-CELL payload. ficlSetEnvD
573*ca987d46SWarner Losh ** makes one with a two-CELL payload.
574*ca987d46SWarner Losh **************************************************************************/
ficlSetEnv(FICL_SYSTEM * pSys,char * name,FICL_UNS value)575*ca987d46SWarner Losh void ficlSetEnv(FICL_SYSTEM *pSys, char *name, FICL_UNS value)
576*ca987d46SWarner Losh {
577*ca987d46SWarner Losh STRINGINFO si;
578*ca987d46SWarner Losh FICL_WORD *pFW;
579*ca987d46SWarner Losh FICL_DICT *envp = pSys->envp;
580*ca987d46SWarner Losh
581*ca987d46SWarner Losh SI_PSZ(si, name);
582*ca987d46SWarner Losh pFW = dictLookup(envp, si);
583*ca987d46SWarner Losh
584*ca987d46SWarner Losh if (pFW == NULL)
585*ca987d46SWarner Losh {
586*ca987d46SWarner Losh dictAppendWord(envp, name, constantParen, FW_DEFAULT);
587*ca987d46SWarner Losh dictAppendCell(envp, LVALUEtoCELL(value));
588*ca987d46SWarner Losh }
589*ca987d46SWarner Losh else
590*ca987d46SWarner Losh {
591*ca987d46SWarner Losh pFW->param[0] = LVALUEtoCELL(value);
592*ca987d46SWarner Losh }
593*ca987d46SWarner Losh
594*ca987d46SWarner Losh return;
595*ca987d46SWarner Losh }
596*ca987d46SWarner Losh
ficlSetEnvD(FICL_SYSTEM * pSys,char * name,FICL_UNS hi,FICL_UNS lo)597*ca987d46SWarner Losh void ficlSetEnvD(FICL_SYSTEM *pSys, char *name, FICL_UNS hi, FICL_UNS lo)
598*ca987d46SWarner Losh {
599*ca987d46SWarner Losh FICL_WORD *pFW;
600*ca987d46SWarner Losh STRINGINFO si;
601*ca987d46SWarner Losh FICL_DICT *envp = pSys->envp;
602*ca987d46SWarner Losh SI_PSZ(si, name);
603*ca987d46SWarner Losh pFW = dictLookup(envp, si);
604*ca987d46SWarner Losh
605*ca987d46SWarner Losh if (pFW == NULL)
606*ca987d46SWarner Losh {
607*ca987d46SWarner Losh dictAppendWord(envp, name, twoConstParen, FW_DEFAULT);
608*ca987d46SWarner Losh dictAppendCell(envp, LVALUEtoCELL(lo));
609*ca987d46SWarner Losh dictAppendCell(envp, LVALUEtoCELL(hi));
610*ca987d46SWarner Losh }
611*ca987d46SWarner Losh else
612*ca987d46SWarner Losh {
613*ca987d46SWarner Losh pFW->param[0] = LVALUEtoCELL(lo);
614*ca987d46SWarner Losh pFW->param[1] = LVALUEtoCELL(hi);
615*ca987d46SWarner Losh }
616*ca987d46SWarner Losh
617*ca987d46SWarner Losh return;
618*ca987d46SWarner Losh }
619*ca987d46SWarner Losh
620*ca987d46SWarner Losh
621*ca987d46SWarner Losh /**************************************************************************
622*ca987d46SWarner Losh f i c l G e t L o c
623*ca987d46SWarner Losh ** Returns the address of the system locals dictionary. This dict is
624*ca987d46SWarner Losh ** only used during compilation, and is shared by all VMs.
625*ca987d46SWarner Losh **************************************************************************/
626*ca987d46SWarner Losh #if FICL_WANT_LOCALS
ficlGetLoc(FICL_SYSTEM * pSys)627*ca987d46SWarner Losh FICL_DICT *ficlGetLoc(FICL_SYSTEM *pSys)
628*ca987d46SWarner Losh {
629*ca987d46SWarner Losh return pSys->localp;
630*ca987d46SWarner Losh }
631*ca987d46SWarner Losh #endif
632*ca987d46SWarner Losh
633*ca987d46SWarner Losh
634*ca987d46SWarner Losh
635*ca987d46SWarner Losh /**************************************************************************
636*ca987d46SWarner Losh f i c l S e t S t a c k S i z e
637*ca987d46SWarner Losh ** Set the stack sizes (return and parameter) to be used for all
638*ca987d46SWarner Losh ** subsequently created VMs. Returns actual stack size to be used.
639*ca987d46SWarner Losh **************************************************************************/
ficlSetStackSize(int nStackCells)640*ca987d46SWarner Losh int ficlSetStackSize(int nStackCells)
641*ca987d46SWarner Losh {
642*ca987d46SWarner Losh if (nStackCells >= FICL_DEFAULT_STACK)
643*ca987d46SWarner Losh defaultStack = nStackCells;
644*ca987d46SWarner Losh else
645*ca987d46SWarner Losh defaultStack = FICL_DEFAULT_STACK;
646*ca987d46SWarner Losh
647*ca987d46SWarner Losh return defaultStack;
648*ca987d46SWarner Losh }
649*ca987d46SWarner Losh
650*ca987d46SWarner Losh
651*ca987d46SWarner Losh /**************************************************************************
652*ca987d46SWarner Losh f i c l T e r m S y s t e m
653*ca987d46SWarner Losh ** Tear the system down by deleting the dictionaries and all VMs.
654*ca987d46SWarner Losh ** This saves you from having to keep track of all that stuff.
655*ca987d46SWarner Losh **************************************************************************/
ficlTermSystem(FICL_SYSTEM * pSys)656*ca987d46SWarner Losh void ficlTermSystem(FICL_SYSTEM *pSys)
657*ca987d46SWarner Losh {
658*ca987d46SWarner Losh if (pSys->dp)
659*ca987d46SWarner Losh dictDelete(pSys->dp);
660*ca987d46SWarner Losh pSys->dp = NULL;
661*ca987d46SWarner Losh
662*ca987d46SWarner Losh if (pSys->envp)
663*ca987d46SWarner Losh dictDelete(pSys->envp);
664*ca987d46SWarner Losh pSys->envp = NULL;
665*ca987d46SWarner Losh
666*ca987d46SWarner Losh #if FICL_WANT_LOCALS
667*ca987d46SWarner Losh if (pSys->localp)
668*ca987d46SWarner Losh dictDelete(pSys->localp);
669*ca987d46SWarner Losh pSys->localp = NULL;
670*ca987d46SWarner Losh #endif
671*ca987d46SWarner Losh
672*ca987d46SWarner Losh while (pSys->vmList != NULL)
673*ca987d46SWarner Losh {
674*ca987d46SWarner Losh FICL_VM *pVM = pSys->vmList;
675*ca987d46SWarner Losh pSys->vmList = pSys->vmList->link;
676*ca987d46SWarner Losh vmDelete(pVM);
677*ca987d46SWarner Losh }
678*ca987d46SWarner Losh
679*ca987d46SWarner Losh ficlFree(pSys);
680*ca987d46SWarner Losh pSys = NULL;
681*ca987d46SWarner Losh return;
682*ca987d46SWarner Losh }
683*ca987d46SWarner Losh
684*ca987d46SWarner Losh
685*ca987d46SWarner Losh /**************************************************************************
686*ca987d46SWarner Losh f i c l S e t V e r s i o n E n v
687*ca987d46SWarner Losh ** Create a double cell environment constant for the version ID
688*ca987d46SWarner Losh **************************************************************************/
ficlSetVersionEnv(FICL_SYSTEM * pSys)689*ca987d46SWarner Losh static void ficlSetVersionEnv(FICL_SYSTEM *pSys)
690*ca987d46SWarner Losh {
691*ca987d46SWarner Losh ficlSetEnvD(pSys, "ficl-version", FICL_VER_MAJOR, FICL_VER_MINOR);
692*ca987d46SWarner Losh ficlSetEnv (pSys, "ficl-robust", FICL_ROBUST);
693*ca987d46SWarner Losh return;
694*ca987d46SWarner Losh }
695*ca987d46SWarner Losh
696