1 /*******************************************************************
3 ** Forth Inspired Command Language - external interface
4 ** Author: John Sadler (john_sadler@alum.mit.edu)
5 ** Created: 19 July 1997
7 *******************************************************************/
9 ** This is an ANS Forth interpreter written in C.
10 ** Ficl uses Forth syntax for its commands, but turns the Forth
11 ** model on its head in other respects.
12 ** Ficl provides facilities for interoperating
13 ** with programs written in C: C functions can be exported to Ficl,
14 ** and Ficl commands can be executed via a C calling interface. The
15 ** interpreter is re-entrant, so it can be used in multiple instances
16 ** in a multitasking system. Unlike Forth, Ficl's outer interpreter
17 ** expects a text block as input, and returns to the caller after each
18 ** text block, so the data pump is somewhere in external code. This
19 ** is more like TCL than Forth.
21 ** Code is written in ANSI C for portability.
24 /* $FreeBSD: src/sys/boot/ficl/ficl.c,v 1.13.2.1 2000/07/06 23:51:45 obrien Exp $ */
46 ** The system builds a global dictionary during its start
47 ** sequence. This is shared by all interpreter instances.
48 ** Therefore only one instance can update the dictionary
49 ** at a time. The system imports a locking function that
50 ** you can override in order to control update access to
51 ** the dictionary. The function is stubbed out by default,
52 ** but you can insert one: #define FICL_MULTITHREAD 1
53 ** and supply your own version of ficlLockDictionary.
55 static FICL_DICT *dp = NULL;
56 static FICL_DICT *envp = NULL;
58 static FICL_DICT *localp = NULL;
60 static FICL_VM *vmList = NULL;
62 static int defaultStack = FICL_DEFAULT_STACK;
63 static int defaultDict = FICL_DEFAULT_DICT;
66 /**************************************************************************
67 f i c l I n i t S y s t e m
68 ** Binds a global dictionary to the interpreter system.
69 ** You specify the address and size of the allocated area.
70 ** After that, ficl manages it.
71 ** First step is to set up the static pointers to the area.
72 ** Then write the "precompiled" portion of the dictionary in.
73 ** The dictionary needs to be at least large enough to hold the
74 ** precompiled part. Try 1K cells minimum. Use "words" to find
75 ** out how much of the dictionary is used at any time.
76 **************************************************************************/
77 void ficlInitSystem(int nDictCells)
91 nDictCells = defaultDict;
93 dp = dictCreateHashed((unsigned)nDictCells, HASHSIZE);
94 envp = dictCreate( (unsigned)FICL_DEFAULT_ENV);
97 ** The locals dictionary is only searched while compiling,
98 ** but this is where speed is most important. On the other
99 ** hand, the dictionary gets emptied after each use of locals
100 ** The need to balance search speed with the cost of the empty
101 ** operation led me to select a single-threaded list...
103 localp = dictCreate( (unsigned)FICL_MAX_LOCALS * CELLS_PER_WORD);
112 /**************************************************************************
114 ** Create a new virtual machine and link it into the system list
115 ** of VMs for later cleanup by ficlTermSystem. If this is the first
116 ** VM to be created, use it to compile the words in softcore.c
117 **************************************************************************/
118 FICL_VM *ficlNewVM(void)
120 FICL_VM *pVM = vmCreate(NULL, defaultStack, defaultStack);
124 ** Borrow the first vm to build the soft words in softcore.c
127 ficlCompileSoftCore(pVM);
134 /**************************************************************************
136 ** Removes the VM in question from the system VM list and deletes the
137 ** memory allocated to it. This is an optional call, since ficlTermSystem
138 ** will do this cleanup for you. This function is handy if you're going to
139 ** do a lot of dynamic creation of VMs.
140 **************************************************************************/
141 void ficlFreeVM(FICL_VM *pVM)
143 FICL_VM *pList = vmList;
149 vmList = vmList->link;
151 else for (pList; pList != 0; pList = pList->link)
153 if (pList->link == pVM)
155 pList->link = pVM->link;
166 /**************************************************************************
168 ** Builds a word into the dictionary.
169 ** Preconditions: system must be initialized, and there must
170 ** be enough space for the new word's header! Operation is
171 ** controlled by ficlLockDictionary, so any initialization
172 ** required by your version of the function (if you overrode
173 ** it) must be complete at this point.
175 ** name -- duh, the name of the word
176 ** code -- code to execute when the word is invoked - must take a single param
177 ** pointer to a FICL_VM
178 ** flags -- 0 or more of F_IMMEDIATE, F_COMPILE, use bitwise OR!
180 **************************************************************************/
181 int ficlBuild(char *name, FICL_CODE code, char flags)
183 int err = ficlLockDictionary(TRUE);
186 assert(dictCellsAvail(dp) > sizeof (FICL_WORD) / sizeof (CELL));
187 dictAppendWord(dp, name, code, flags);
189 ficlLockDictionary(FALSE);
194 /**************************************************************************
196 ** Evaluates a block of input text in the context of the
197 ** specified interpreter. Emits any requested output to the
198 ** interpreter's output function.
200 ** Contains the "inner interpreter" code in a tight loop
202 ** Returns one of the VM_XXXX codes defined in ficl.h:
203 ** VM_OUTOFTEXT is the normal exit condition
204 ** VM_ERREXIT means that the interp encountered a syntax error
205 ** and the vm has been reset to recover (some or all
206 ** of the text block got ignored
207 ** VM_USEREXIT means that the user executed the "bye" command
208 ** to shut down the interpreter. This would be a good
209 ** time to delete the vm, etc -- or you can ignore this
211 **************************************************************************/
212 int ficlExec(FICL_VM *pVM, char *pText)
214 return ficlExecC(pVM, pText, -1);
217 int ficlExecC(FICL_VM *pVM, char *pText, FICL_INT size)
219 static FICL_WORD *pInterp = NULL;
227 pInterp = ficlLookup("interpret");
233 size = strlen(pText);
235 vmPushTib(pVM, pText, size, &saveTib);
238 ** Save and restore VM's jmp_buf to enable nested calls to ficlExec
240 oldState = pVM->pState;
241 pVM->pState = &vmState; /* This has to come before the setjmp! */
242 except = setjmp(vmState);
250 pVM->runningWord->code(pVM);
253 { /* set VM up to interpret text */
254 vmPushIP(pVM, &pInterp);
262 except = VM_OUTOFTEXT;
268 if ((pVM->state != COMPILE) && (pVM->sourceID.i == 0))
269 ficlTextOut(pVM, FICL_PROMPT, 0);
278 if (pVM->state == COMPILE)
280 dictAbortDefinition(dp);
282 dictEmpty(localp, localp->pForthWords->size);
291 default: /* user defined exit code?? */
292 if (pVM->state == COMPILE)
294 dictAbortDefinition(dp);
296 dictEmpty(localp, localp->pForthWords->size);
299 dictResetSearchOrder(dp);
304 pVM->pState = oldState;
305 vmPopTib(pVM, &saveTib);
309 /**************************************************************************
311 ** reads in text from file fd and passes it to ficlExec()
312 * returns VM_OUTOFTEXT on success or the ficlExec() error code on
316 int ficlExecFD(FICL_VM *pVM, int fd)
319 int nLine = 0, rval = VM_OUTOFTEXT;
324 pVM->sourceID.i = fd;
326 /* feed each line to ficlExec */
331 while ((status = read(fd, &ch, 1)) > 0 && ch != '\n')
339 rval = ficlExecC(pVM, cp, i);
340 if(rval != VM_QUIT && rval != VM_USEREXIT && rval != VM_OUTOFTEXT)
347 ** Pass an empty line with SOURCE-ID == -1 to flush
348 ** any pending REFILLs (as required by FILE wordset)
350 pVM->sourceID.i = -1;
357 /**************************************************************************
359 ** Given a pointer to a FICL_WORD, push an inner interpreter and
360 ** execute the word to completion. This is in contrast with vmExecute,
361 ** which does not guarantee that the word will have completed when
362 ** the function returns (ie in the case of colon definitions, which
363 ** need an inner interpreter to finish)
365 ** Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
366 ** exit condition is VM_INNEREXIT, ficl's private signal to exit the
367 ** inner loop under normal circumstances. If another code is thrown to
368 ** exit the loop, this function will re-throw it if it's nested under
369 ** itself or ficlExec.
371 ** NOTE: this function is intended so that C code can execute ficlWords
372 ** given their address in the dictionary (xt).
373 **************************************************************************/
374 int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord)
376 static FICL_WORD *pQuit = NULL;
382 pQuit = ficlLookup("exit-inner");
388 ** Save and restore VM's jmp_buf to enable nested calls
390 oldState = pVM->pState;
391 pVM->pState = &vmState; /* This has to come before the setjmp! */
392 except = setjmp(vmState);
397 vmPushIP(pVM, &pQuit);
402 vmExecute(pVM, pWord);
416 default: /* user defined exit code?? */
419 pVM->pState = oldState;
420 vmThrow(pVM, except);
425 pVM->pState = oldState;
430 /**************************************************************************
432 ** Look in the system dictionary for a match to the given name. If
433 ** found, return the address of the corresponding FICL_WORD. Otherwise
435 **************************************************************************/
436 FICL_WORD *ficlLookup(char *name)
440 return dictLookup(dp, si);
444 /**************************************************************************
445 f i c l G e t D i c t
446 ** Returns the address of the system dictionary
447 **************************************************************************/
448 FICL_DICT *ficlGetDict(void)
454 /**************************************************************************
456 ** Returns the address of the system environment space
457 **************************************************************************/
458 FICL_DICT *ficlGetEnv(void)
464 /**************************************************************************
466 ** Create an environment variable with a one-CELL payload. ficlSetEnvD
467 ** makes one with a two-CELL payload.
468 **************************************************************************/
469 void ficlSetEnv(char *name, FICL_UNS value)
475 pFW = dictLookup(envp, si);
479 dictAppendWord(envp, name, constantParen, FW_DEFAULT);
480 dictAppendCell(envp, LVALUEtoCELL(value));
484 pFW->param[0] = LVALUEtoCELL(value);
490 void ficlSetEnvD(char *name, FICL_UNS hi, FICL_UNS lo)
495 pFW = dictLookup(envp, si);
499 dictAppendWord(envp, name, twoConstParen, FW_DEFAULT);
500 dictAppendCell(envp, LVALUEtoCELL(lo));
501 dictAppendCell(envp, LVALUEtoCELL(hi));
505 pFW->param[0] = LVALUEtoCELL(lo);
506 pFW->param[1] = LVALUEtoCELL(hi);
513 /**************************************************************************
515 ** Returns the address of the system locals dictionary. This dict is
516 ** only used during compilation, and is shared by all VMs.
517 **************************************************************************/
519 FICL_DICT *ficlGetLoc(void)
527 /**************************************************************************
528 f i c l S e t S t a c k S i z e
529 ** Set the stack sizes (return and parameter) to be used for all
530 ** subsequently created VMs. Returns actual stack size to be used.
531 **************************************************************************/
532 int ficlSetStackSize(int nStackCells)
534 if (nStackCells >= FICL_DEFAULT_STACK)
535 defaultStack = nStackCells;
537 defaultStack = FICL_DEFAULT_STACK;
543 /**************************************************************************
544 f i c l T e r m S y s t e m
545 ** Tear the system down by deleting the dictionaries and all VMs.
546 ** This saves you from having to keep track of all that stuff.
547 **************************************************************************/
548 void ficlTermSystem(void)
564 while (vmList != NULL)
566 FICL_VM *pVM = vmList;
567 vmList = vmList->link;