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 $ */
25 /* $DragonFly: src/sys/boot/ficl/ficl.c,v 1.2 2003/06/17 04:28:17 dillon Exp $ */
47 ** The system builds a global dictionary during its start
48 ** sequence. This is shared by all interpreter instances.
49 ** Therefore only one instance can update the dictionary
50 ** at a time. The system imports a locking function that
51 ** you can override in order to control update access to
52 ** the dictionary. The function is stubbed out by default,
53 ** but you can insert one: #define FICL_MULTITHREAD 1
54 ** and supply your own version of ficlLockDictionary.
56 static FICL_DICT *dp = NULL;
57 static FICL_DICT *envp = NULL;
59 static FICL_DICT *localp = NULL;
61 static FICL_VM *vmList = NULL;
63 static int defaultStack = FICL_DEFAULT_STACK;
64 static int defaultDict = FICL_DEFAULT_DICT;
67 /**************************************************************************
68 f i c l I n i t S y s t e m
69 ** Binds a global dictionary to the interpreter system.
70 ** You specify the address and size of the allocated area.
71 ** After that, ficl manages it.
72 ** First step is to set up the static pointers to the area.
73 ** Then write the "precompiled" portion of the dictionary in.
74 ** The dictionary needs to be at least large enough to hold the
75 ** precompiled part. Try 1K cells minimum. Use "words" to find
76 ** out how much of the dictionary is used at any time.
77 **************************************************************************/
78 void ficlInitSystem(int nDictCells)
92 nDictCells = defaultDict;
94 dp = dictCreateHashed((unsigned)nDictCells, HASHSIZE);
95 envp = dictCreate( (unsigned)FICL_DEFAULT_ENV);
98 ** The locals dictionary is only searched while compiling,
99 ** but this is where speed is most important. On the other
100 ** hand, the dictionary gets emptied after each use of locals
101 ** The need to balance search speed with the cost of the empty
102 ** operation led me to select a single-threaded list...
104 localp = dictCreate( (unsigned)FICL_MAX_LOCALS * CELLS_PER_WORD);
113 /**************************************************************************
115 ** Create a new virtual machine and link it into the system list
116 ** of VMs for later cleanup by ficlTermSystem. If this is the first
117 ** VM to be created, use it to compile the words in softcore.c
118 **************************************************************************/
119 FICL_VM *ficlNewVM(void)
121 FICL_VM *pVM = vmCreate(NULL, defaultStack, defaultStack);
125 ** Borrow the first vm to build the soft words in softcore.c
128 ficlCompileSoftCore(pVM);
135 /**************************************************************************
137 ** Removes the VM in question from the system VM list and deletes the
138 ** memory allocated to it. This is an optional call, since ficlTermSystem
139 ** will do this cleanup for you. This function is handy if you're going to
140 ** do a lot of dynamic creation of VMs.
141 **************************************************************************/
142 void ficlFreeVM(FICL_VM *pVM)
144 FICL_VM *pList = vmList;
150 vmList = vmList->link;
152 else for (pList; pList != 0; pList = pList->link)
154 if (pList->link == pVM)
156 pList->link = pVM->link;
167 /**************************************************************************
169 ** Builds a word into the dictionary.
170 ** Preconditions: system must be initialized, and there must
171 ** be enough space for the new word's header! Operation is
172 ** controlled by ficlLockDictionary, so any initialization
173 ** required by your version of the function (if you overrode
174 ** it) must be complete at this point.
176 ** name -- duh, the name of the word
177 ** code -- code to execute when the word is invoked - must take a single param
178 ** pointer to a FICL_VM
179 ** flags -- 0 or more of F_IMMEDIATE, F_COMPILE, use bitwise OR!
181 **************************************************************************/
182 int ficlBuild(char *name, FICL_CODE code, char flags)
184 int err = ficlLockDictionary(TRUE);
187 assert(dictCellsAvail(dp) > sizeof (FICL_WORD) / sizeof (CELL));
188 dictAppendWord(dp, name, code, flags);
190 ficlLockDictionary(FALSE);
195 /**************************************************************************
197 ** Evaluates a block of input text in the context of the
198 ** specified interpreter. Emits any requested output to the
199 ** interpreter's output function.
201 ** Contains the "inner interpreter" code in a tight loop
203 ** Returns one of the VM_XXXX codes defined in ficl.h:
204 ** VM_OUTOFTEXT is the normal exit condition
205 ** VM_ERREXIT means that the interp encountered a syntax error
206 ** and the vm has been reset to recover (some or all
207 ** of the text block got ignored
208 ** VM_USEREXIT means that the user executed the "bye" command
209 ** to shut down the interpreter. This would be a good
210 ** time to delete the vm, etc -- or you can ignore this
212 **************************************************************************/
213 int ficlExec(FICL_VM *pVM, char *pText)
215 return ficlExecC(pVM, pText, -1);
218 int ficlExecC(FICL_VM *pVM, char *pText, FICL_INT size)
220 static FICL_WORD *pInterp = NULL;
228 pInterp = ficlLookup("interpret");
234 size = strlen(pText);
236 vmPushTib(pVM, pText, size, &saveTib);
239 ** Save and restore VM's jmp_buf to enable nested calls to ficlExec
241 oldState = pVM->pState;
242 pVM->pState = &vmState; /* This has to come before the setjmp! */
243 except = setjmp(vmState);
251 pVM->runningWord->code(pVM);
254 { /* set VM up to interpret text */
255 vmPushIP(pVM, &pInterp);
263 except = VM_OUTOFTEXT;
269 if ((pVM->state != COMPILE) && (pVM->sourceID.i == 0))
270 ficlTextOut(pVM, FICL_PROMPT, 0);
279 if (pVM->state == COMPILE)
281 dictAbortDefinition(dp);
283 dictEmpty(localp, localp->pForthWords->size);
292 default: /* user defined exit code?? */
293 if (pVM->state == COMPILE)
295 dictAbortDefinition(dp);
297 dictEmpty(localp, localp->pForthWords->size);
300 dictResetSearchOrder(dp);
305 pVM->pState = oldState;
306 vmPopTib(pVM, &saveTib);
310 /**************************************************************************
312 ** reads in text from file fd and passes it to ficlExec()
313 * returns VM_OUTOFTEXT on success or the ficlExec() error code on
317 int ficlExecFD(FICL_VM *pVM, int fd)
320 int nLine = 0, rval = VM_OUTOFTEXT;
325 pVM->sourceID.i = fd;
327 /* feed each line to ficlExec */
332 while ((status = read(fd, &ch, 1)) > 0 && ch != '\n')
340 rval = ficlExecC(pVM, cp, i);
341 if(rval != VM_QUIT && rval != VM_USEREXIT && rval != VM_OUTOFTEXT)
348 ** Pass an empty line with SOURCE-ID == -1 to flush
349 ** any pending REFILLs (as required by FILE wordset)
351 pVM->sourceID.i = -1;
358 /**************************************************************************
360 ** Given a pointer to a FICL_WORD, push an inner interpreter and
361 ** execute the word to completion. This is in contrast with vmExecute,
362 ** which does not guarantee that the word will have completed when
363 ** the function returns (ie in the case of colon definitions, which
364 ** need an inner interpreter to finish)
366 ** Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
367 ** exit condition is VM_INNEREXIT, ficl's private signal to exit the
368 ** inner loop under normal circumstances. If another code is thrown to
369 ** exit the loop, this function will re-throw it if it's nested under
370 ** itself or ficlExec.
372 ** NOTE: this function is intended so that C code can execute ficlWords
373 ** given their address in the dictionary (xt).
374 **************************************************************************/
375 int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord)
377 static FICL_WORD *pQuit = NULL;
383 pQuit = ficlLookup("exit-inner");
389 ** Save and restore VM's jmp_buf to enable nested calls
391 oldState = pVM->pState;
392 pVM->pState = &vmState; /* This has to come before the setjmp! */
393 except = setjmp(vmState);
398 vmPushIP(pVM, &pQuit);
403 vmExecute(pVM, pWord);
417 default: /* user defined exit code?? */
420 pVM->pState = oldState;
421 vmThrow(pVM, except);
426 pVM->pState = oldState;
431 /**************************************************************************
433 ** Look in the system dictionary for a match to the given name. If
434 ** found, return the address of the corresponding FICL_WORD. Otherwise
436 **************************************************************************/
437 FICL_WORD *ficlLookup(char *name)
441 return dictLookup(dp, si);
445 /**************************************************************************
446 f i c l G e t D i c t
447 ** Returns the address of the system dictionary
448 **************************************************************************/
449 FICL_DICT *ficlGetDict(void)
455 /**************************************************************************
457 ** Returns the address of the system environment space
458 **************************************************************************/
459 FICL_DICT *ficlGetEnv(void)
465 /**************************************************************************
467 ** Create an environment variable with a one-CELL payload. ficlSetEnvD
468 ** makes one with a two-CELL payload.
469 **************************************************************************/
470 void ficlSetEnv(char *name, FICL_UNS value)
476 pFW = dictLookup(envp, si);
480 dictAppendWord(envp, name, constantParen, FW_DEFAULT);
481 dictAppendCell(envp, LVALUEtoCELL(value));
485 pFW->param[0] = LVALUEtoCELL(value);
491 void ficlSetEnvD(char *name, FICL_UNS hi, FICL_UNS lo)
496 pFW = dictLookup(envp, si);
500 dictAppendWord(envp, name, twoConstParen, FW_DEFAULT);
501 dictAppendCell(envp, LVALUEtoCELL(lo));
502 dictAppendCell(envp, LVALUEtoCELL(hi));
506 pFW->param[0] = LVALUEtoCELL(lo);
507 pFW->param[1] = LVALUEtoCELL(hi);
514 /**************************************************************************
516 ** Returns the address of the system locals dictionary. This dict is
517 ** only used during compilation, and is shared by all VMs.
518 **************************************************************************/
520 FICL_DICT *ficlGetLoc(void)
528 /**************************************************************************
529 f i c l S e t S t a c k S i z e
530 ** Set the stack sizes (return and parameter) to be used for all
531 ** subsequently created VMs. Returns actual stack size to be used.
532 **************************************************************************/
533 int ficlSetStackSize(int nStackCells)
535 if (nStackCells >= FICL_DEFAULT_STACK)
536 defaultStack = nStackCells;
538 defaultStack = FICL_DEFAULT_STACK;
544 /**************************************************************************
545 f i c l T e r m S y s t e m
546 ** Tear the system down by deleting the dictionaries and all VMs.
547 ** This saves you from having to keep track of all that stuff.
548 **************************************************************************/
549 void ficlTermSystem(void)
565 while (vmList != NULL)
567 FICL_VM *pVM = vmList;
568 vmList = vmList->link;