|
|
1.1 root 1: .nr Cl 7
2: .ND "October 29, 1979"
3: .TL "20239-7048" "40295-2"
4: Guide To The Internals Of
5: .I Bite
6: .AU "R. B. Drake" RBD WH 2425 4163 8C-005
7: .TM 79-2425-5
8: .AS 1
9: .I Bite,
10: .B B asic
11: .B I nterpreter
12: for
13: .B T esting
14: and
15: .B E ngineering,
16: with very few exceptions, follows the syntax of the original Dartmouth
17: .I BASIC
18: It is unique because it is written in C,
19: it's source code is available, new commands to fit any desired purpose
20: can be written in C and easily added,
21: or for that matter, any existing routine such as those in section 3 of
22: the
23: .I "UNIX Users Manual"
24: can be linked in with
25: .I Bite .
26: The authors purpose was to
27: provide an interpretive language to control automated test sets where
28: the basic control flow operations,
29: .I Print
30: statements,
31: .I If
32: statements
33: etc. would be standard
34: .I BASIC
35: but where additional commands to control
36: test instruments would be added as needed.
37: With this approach, the language can be tailored to fit virtually any test
38: set configuration, or molded to perform any
39: kind of task where an interpretive language is suitable.
40: .P
41: Presented here is a tutorial which describes in detail how to add/remove
42: commands and functions to/from the language and how to use the vital internal
43: functions which store and retrieve variables, alter program flow, parse
44: expressions,
45: evaluate expressions, print diagnostic messages etc.
46: .AE
47: .OK "Basic" "Interpreters" "Bite" "Automated Testing" "Product Evaluation Project"
48: .MT 1
49: .SA 0
50: .H 1 INTRODUCTION
51: .H 2 Purpose
52: This memorandum is for the C language programmer who wishes to modify or
53: extend
54: .I bite
55: in order to tailor it to some special purpose. It is presumed that the
56: reader is already fluent in C and
57: .I UNIX*
58: .FS *
59: UNIX is a Trademark of Bell Laboratories.
60: .FE
61: and familiar with
62: .I bite .\*F
63: .FS
64: Hawkins J. P.,"BITE Users Guide",BTL,TM-79-2425-4
65: .FE
66: We will follow the method of tutoring by example so that when the reader
67: is finished he should be able to:
68: .BL
69: .LI
70: Add or remove commands from the language.
71: .LI
72: Add or remove function subroutines.
73: .LI
74: Understand how to use certain important internal function subroutines.
75: .LI
76: Have a general understanding of how
77: .I bite
78: works internally.
79: .LI
80: Have a general understanding of why the authors made certain decisions
81: regarding the way
82: .I bite
83: is implemented.
84: .H 1 "BITE RELEASE TAPE"
85: .I Bite
86: is available on magnetic tape from the author or J. P. Hawkins of department
87: 2425.
88: The
89: .I Bite
90: release tape is divided into four major directories,
91: doc,include,lib and src. Doc of course contains all currently
92: available documentation. Include contains all of the header (.h)
93: files necessary to recompile.
94: Lib
95: contains archive files containing all of the object modules
96: which may be used to create a new load module with perhaps some
97: new or changed routines without recompiling everything.
98: Src contains five directories,
99: each of which contains source code for all of the files
100: contained in the Lib archives. In addition to source code each
101: directory under "src" contains one or more shell scripts intended
102: to build a new archive or recompile individual modules within
103: that particular source directory and link the new module with the
104: existing ones in the archives. In general the scripts to build
105: new archives are called "build.sh" and the ones to recompile individual
106: modules are called "compile.sh". In the "src/bite"
107: directory there are separate "build" and "compile" scripts for
108: .I bite ,
109: .I bitex
110: and
111: .I bitem
112: Where
113: .I bite
114: is intended to run under PWB/UNIX version 2.0.
115: .I Bitex
116: runs stand alone on a PDP-11/03 micro computer and contains drivers for the
117: IBV11-A (IEEE 488 instrumentt bus) and certain test instruments utilized by
118: Dept. 2425 for automated testing. Due to space considerations, this version
119: does not have a complete math package (i.e. sin(),cos(),tan() etc.).
120: .I Bitem
121: also runs stand alone on a PDP 11/03 but contains no instrument bus drivers
122: and does contain a complete math package.
123: Both PDP 11/03 versions are compiled using a special floating point precompiler,
124: most of which was obtained from Ron Hardin of Columbus Laboratories and
125: are linked together with a set of subroutines referred to as the
126: file system interface (FSI). FSI interfaces
127: .I bite
128: with the file system and contains all necessary system utilities
129: such that, on the PDP-11/03,
130: .I bite
131: runs with no operating system in memory. Versions of FSI are available for
132: Digital Equipment Corp. RX-01 floppy disks and RL-01 hard disks. The "src"
133: directory contains the source code for both the special compiler and FSI.
134: The tape supplied is created by the "cpio" command.
135: To load the tape, create a directory where it is desired to
136: place the software, mount the tape and issue:
137: .DF 1 0
138: cpio -id < /dev/mt0
139: where 0 is mag tape unit zero.
140: .DE
141: .H 1 "ADDING COMMANDS"
142: .H 2 "General Format Of bite Instructions"
143: In
144: .I bite
145: the users
146: source code is operated on by a special purpose editor
147: .I bed.c
148: and stored as pseudo object code in large character array hereafter
149: referred to as the
150: .I "Program Buffer" .
151: Each instruction consists of four fields, which are arranged as follows:
152: .VL 8 5
153: .LI 1)
154: The first two bytes of each instruction are interpreted as an integer
155: which is the line number of the instruction.
156: .LI 2)
157: The third byte is an excess 200 tab counter. One or more tabs may be inserted
158: between the line number and the command. Rather than physically storing
159: these tabs in the program buffer, all that is kept is a count of how many
160: tabs. A tab count of zero indicates that one blank is to be used. If the user
161: enters more than one blank, the additional ones will be stripped away.
162: Excess 200 means that 200 (base 8) is added to the actual tab count. This is
163: done so that byte three will either be zero or a number greater than 200 and
164: as such will never look like a valid
165: .I ascii
166: character. Since it is not a valid character, it can be and is used as a
167: beginning of line delimiter. Such a delimiter is needed whenever the buffer
168: pointer must be moved backwards to the beginning of the previous line as is
169: done by the "-" command.
170: .LI 3)
171: The fourth byte is interpreted as an integer hereafter referred to as the
172: .I opcode
173: which is an index into a table of function addresses hereafter called the
174: .I "Command Dispatch Table"
175: which is used to call the appropriate function via an indirect function call.
176: .LI 4)
177: The fourth field is variable in length beginning with the fifth byte
178: and continuing through a
179: .I NULL
180: byte. This
181: .I expression
182: field is pure ascii text, except that all blanks are
183: stripped out of it and certain
184: .I keywords
185: (See 3.6.2)
186: are encoded into a single ascii character in the
187: range of 0 to 37 base 8 i.e. the control characters.
188: The
189: .I expression
190: field
191: is used to pass variable information to the function called.
192: .LE
193: .SP
194: Thus the name of a command (if, print, for etc.) is never stored
195: in the program buffer as
196: .I ascii
197: text but rather is encoded into a single integer number.
198: Internally then the
199: .I bite
200: command is executed by the C language statement
201: .DF 2 0
202: bascall(opcode)
203: .DE
204: where
205: .I bascall
206: is a macro defined within the
207: .I bite
208: header file
209: .I bas.h
210: and has the following form:
211: .DF 1 0
212: #define bascall(opcode) ((*cmdtbl[opcode].function)())
213: .DE
214: and
215: .I cmdtbl
216: is defined as follows:
217: .DF 1 0
218: struct {
219: char *cmdtxt /*pointer to the command name */
220: int (*function)(); /*pointer to the command function*/
221: } cmdtbl[];
222: .DE
223: The expression field of the instruction is decoded by the function called.
224: Conversion of the first four bytes of each instruction is handled by
225: copying them into a "union" defined in
226: .I bas.h
227: as follows:
228: .DF 1 0
229: /* line number and command code structure aligned with 4 bytes for storage*/
230: union bascmd
231: {
232: struct
233: {
234: int linno;
235: struct
236: {
237: char hibyte;
238: char lobyte;
239: } opcode;
240: };
241: char byte[4];
242: };
243: .DE
244: Each time an instruction is fetched from the program buffer (See 3.3.1)
245: the first four bytes are copied into a golbally accessable
246: instance "inst" of this "union" defined
247: in "bed.c". This eliminates the need to be concerned with byte allignment
248: in the program buffer. Without this "union", each instruction would have to
249: begin on an even byte boundary so that the first two bytes could be referred
250: to as an integer. We pay a small execution time price, by requireing the
251: copy but save space in the program buffer by not requireing the byte allignment.
252: .H 2 Encode.c
253: To add an instruction to
254: .I bite ,
255: first edit the
256: .I encode.c
257: module to define the name of the function and add its name and address
258: into the command dispatch table.
259: The order in which commands are defined or appear in the dispatch table
260: is arbitrary and totally unimportant to
261: .I bite .
262: The list of definitions begins with the comment:
263: .DF 1 0
264: BASIC INTERPRETER COMMAND TABLE
265: .DE
266: .SP
267: simply follow the syntax of the other definitions and add in the new one.
268: The list entries in the dispatch table begins with the line:
269: .DF 1 0
270: struct COMMAND cmdtbl[]
271: .DE
272: Each entry in the dispatch table consists of two items:
273: .VL 8 5
274: .LI 1)
275: The name of the command as it will be written in the
276: .I bite
277: source statement.
278: .LI 2)
279: The name of the function to be called. These two names may or may not
280: be the same. Syntactically it's easier for human beings to read the program
281: if the names are the same but quite often other considerations such as
282: conflicts with
283: .I Unix
284: system names etc. force them to be different.
285: .LE
286: .SP
287: The next step is to write the function in C, then compile and link it
288: with
289: .I bite
290: using the appropriate shell script (See section 2).
291: .H 3 Example
292: We will take a look at the most simple command in
293: .I bite
294: "rem".
295: It's definition appears in the command table as:
296: .DF 2 0
297: int no_op();
298: .DE
299: It's entry in the dispatch table is:
300: .DF 2 0
301: {"rem", no_op},
302: .DE
303: and the C language function is as follows:
304: .DF 1 0
305: no_op()
306: {
307: return(0);
308: }
309: .DE
310: Obviously, "rem" is a
311: .I bite
312: no-op, i.e. it does nothing.
313: The "return(0)" is absolutely necessary however. An unpublished and therefore
314: often unknown to the new C programmer (veterans have learned it, usually
315: the hard way) fact is that although C does not require a "return" statement
316: at the end of a function, the value returned by a function without a
317: "return" statement is undefined i.e. it could be anything. Since the run time
318: monitor of
319: .I bite
320: tests the value returned by all functions, all must have a specific
321: returned value. A value of -1 indicates that a fatal error was detected
322: during the execution of the function and the run is terminated. A value
323: of 0 indicates that no fatal errors were detected.
324: Every time a source statement such as:
325: .DF 2 0
326: 10 rem this is a comment
327: .DE
328: is encountered,
329: the "no_op" function is called.
330: It is up to "no_op" to evaluate the expression "this is a comment", however,
331: comments don't require any action so "no_op" just returns without doing
332: anything.
333: .H 2 fetch.c
334: The "run" function of
335: .I bite
336: uses an internal function called
337: .I fetch
338: to retrieve instructions from the program buffer. The call to
339: fetch is as follows:
340: .DF 1 0
341: fetch(n,&ptr);
342: .DE
343: where n is an integer with the following meaning:
344: .VL 15 5
345: .LI "n= -1"
346: Get the first instruction in the program buffer and set "ptr" ("ptr" is
347: defined as "char *ptr") to point to the next sequential line, which in
348: this case, will be the second line in the program buffer.
349: .LI "n= 0"
350: Get the line pointed to by "ptr" and set "ptr" to point to the next line.
351: .LI "n= line #"
352: Get line number "n" and set "ptr" to point to the next line.
353: If line number "n" does not exist, get the line with the next
354: higher line number or the last line of the program if there
355: are no higher line numbers.
356: .LE
357: .SP
358: .I Fetch
359: takes the following action:
360: .VL 8 5
361: .LI 1)
362: Copies the first four bytes into the instruction "union" (See 3.1).
363: .LI 2)
364: Sets a global character pointer "expr" to point to the buffer position where
365: the expression field of the fetched line begins.
366: .LI 3)
367: As noted above, updates the value of the argument "ptr".
368: .LI 4)
369: Sets a global character pointer "curptr" to point to the beginning
370: of the fetched line.
371: .LI 5)
372: Returns an integer with the following meaning: 0 if the operation was
373: 100% successful, -1 if the operation was a total failure, -2 if a line
374: was found but not the one asked for.
375: .LE
376: .SP
377: Thus, the line number, opcode, tab count,
378: and expression field are globally available
379: after a call to
380: .I fetch .
381: It should be noted here that if the function being called via
382: .I opcode
383: must for some reason modify the character string pointed to by "expr"
384: it
385: .B must
386: copy the expression to a local place otherwise it will be permanently
387: modifying the program buffer which can have disastrous side affects.
388: In most cases, however, it is not necessary for the function to modify
389: the expression field, rather it need only evaluate what's there.
390: .H 2 "Run Time Monitor"
391: Before continuing the discussion of
392: .I "Expression Evaluation"
393: it will be helpful for the reader to understand a little of how
394: program execution is controlled.
395: The "run" function in
396: .I bite
397: which is sometimes referred to as
398: .I "The Run Time Monitor"
399: is first called by typing "run" instead of a line number followed
400: by an operation and expression. The
401: .I bite
402: editor
403: .I bed
404: which is in control when
405: .I bite
406: is first invoked recognizes it as a command for immediate execution
407: because it is not preceded by a line number and calls it.
408: "run" first carries out a few housekeeping chores then fetches
409: the first instruction in the program buffer and executes it via
410: .I bascall.
411: Assuming that the first instruction does not return a -1 (fatal error)
412: "run" then begins sequentially fetching and calling each instruction in order.
413: This continues until
414: .I fetch
415: returns a non zero value (meaning end of file) or until a fatal error
416: is returned by one of the instruction functions.
417: In either case when "run" executes it's return instruction,
418: control is returned to
419: .I bed.
420: .H 3 Example
421: Another rather short
422: .I bite
423: function
424: .I goto
425: will be used to demonstrate the features discussed so far. The source
426: of
427: .I goto
428: is as follows:
429: .DF 1 0
430: /* ///// goto routine //////
431: /* part of Drake Hawkins Basic
432: /* R. B. Drake /////
433: /* copyright Bell Telephone Laboratories ///
434: /* Whippany N. J. ////////////
435: */
436: #include <bas.h>
437: __goto()
438: {
439: int savno,start;
440: extern char *ptr,*curptr;
441: savno = inst.linno;
442: start=atoi(expr);
443: fetch(start,&ptr);
444: if(inst.linno != start)
445: {
446: error(savno,0);
447: inst.linno = savno;
448: return(-1);
449: }
450: ptr = curptr;
451: return(0);
452: }
453: .DE
454: "savno" is used to save the current line number for possible use in
455: a call to the error printing routine (See 3.5). "start" is
456: used to hold the line number gleaned from the expression field. "*ptr"
457: is the global instruction pointer used by the run time monitor and
458: "*curptr" is set by
459: .I fetch
460: (see 3.3).
461: The statement
462: .DF 1 0
463: start=atoi(expr);
464: .DE
465: .SP
466: Converts the line number which must be in the expression field i.e.
467: .DF 1 0
468: 10 rem the 40 in line 30 is the
469: 20 rem line number in the expression field
470: 30 goto 40
471: 40 end
472: .DE
473: The statement
474: .DF 2 0
475: fetch(start,&ptr);
476: .DE
477: fetches the line asked for and the code which follows that checks to
478: see that the line fetched is indeed the one requested. If the line
479: fetched is not the one requested, then the "goto" refers to a
480: non-existent line in which case an error message is printed and a
481: -1 (function detected a fatal error) is returned to the run time
482: monitor. Otherwise, *ptr is set to *curptr and a zero is returned
483: to the run time monitor. Setting *ptr to *curptr primes
484: .I fetch
485: so that the next time it is called with a
486: .DF 1 0
487: fetch(0,&ptr);
488: .DE
489: which is what the run time monitor will do when it gets control again,
490: the line indicated in the "goto" is the one that will be fetched.
491: .H 2 Error.c
492: All printing of error messages in
493: .I bite
494: is done by calling a special error printing routine as follows:
495: .DF 2 0
496: error(linno,num);
497: .DE
498: .B Notes:
499: .VL 8 5
500: .LI 1)
501: If
502: .I linno
503: is greater than zero, it prints:
504: .DF 1 0
505: ERROR LINE
506: .I linno
507: MESSAGE INDICATED BY
508: .I num
509: .DE
510: .SP
511: where
512: .I num
513: is an index number into an array of pointers to character
514: strings.
515: The array of pointers to character strings is defined within
516: .I error.c
517: as follows:
518: .DF
519: static char *mesg[]
520: {
521: "REFERS TO A NON EXISTING LINE NUMBER", /* 0 */
522: "UNRECOGNIZABLE OPERATION", /* 1 */
523: .
524: .
525: .
526: .
527: "EXPRESSION YIELDS AN IMPOSSIBLE VALUE", /* 38 */
528: };
529: .DE
530: So that if the call to
531: .I error
532: was
533: .DF 2 0
534: error(10,0);
535: .DE
536: The message printed would be:
537: .DF 1 0
538: ERROR LINE 10 REFERS TO A NON EXISTING LINE NUMBER
539: .DE
540: The numbers inside the comment delimiters i.e. "/* 0 */" correspond
541: to the appropriate array index number to access that message. This is
542: indispensable to the programmer and of utmost importance that anyone
543: modifying
544: .I error.c
545: maintain that convention.
546: It should also be noted that since the messages are referred to by
547: their index number, the position of the current messages can not
548: be changed. for example if one message is deleted, all the messages
549: above it would be in the wrong position, and any calls to
550: .I error
551: with intent to print those messages would print the wrong one.
552: Therefore any new messages must be added to the end of the list.
553: .LI 2)
554: If
555: .I num
556: is zero, the printing of the line number is suppressed. This feature
557: is used in commands being executed in immediate mode where there is
558: no line number to reference.
559: .LI 3)
560: Error numbers less than 100 are reserved for basic
561: .I bite .
562: That is, that portion of
563: .I bite
564: which is common to all versions. While error numbers 100 and greater
565: are used for user added instructions. This is convenient for the use
566: of conditional compile instructions so that several versions may be
567: kept in a single source file and the appropriate version can be compiled
568: by simply changing one
569: .I define
570: statement
571: in a header file.
572: For example, the current source tape of
573: .I bite
574: contains a conditional variable "LSX". If "LSX" is defined in the header
575: file
576: .I bas.h ,
577: then a version for the PDP-11/03 computer containing many test set
578: instructions is compiled. If "LSX" is not defined then a version
579: for the PDP-11/70 computer containing no test set instructions or
580: the error messages that go with them is compiled.
581: .LE
582: .SP
583: Printing error messages in this fashion as opposed to just printing them
584: via
585: .I printf
586: statements where they occur has a number of advantages.
587: .VL 8 5
588: .LI 1)
589: It gathers all of the error messages in one place where they are easily
590: seen and therefore there is no temptation to have the same or similar
591: messages appearing in several different places within
592: .I bite
593: which would be an unnecessary waste of core space.
594: .LI 2)
595: It standardizes the format in which errors are printed.
596: .LI 3)
597: If core space should become tight as it is in the LSI version of
598: .I bite ,
599: all error messages can be reduced to the printing of an error number
600: which the user would have to look up, thereby saving the space required
601: to store the messages. While this would be less convenient, it would be
602: less of a sacrifice than having to eliminate needed functions. Since
603: all the messages are in one place, only one routine
604: .I error.c
605: would have to be modified to accomplish this.
606: .LE
607: .SP
608: .H 2 "Parsing Expressions"
609: .H 3 prncpy
610: Many commands in
611: .I bite
612: are followed by an expression field consisting of a list of items
613: separated by commas such as:
614: .DF 1 0
615: 10 input a,b,c
616: 20 on a goto 30,40,50
617: 30 print a,b,c
618: 40 stop
619: 50 goto 10
620: .DE
621: The comma is always the delimiter separating items except when it is
622: within parenthesis as in:
623: .DF 1 0
624: 10 read a(1,1),a(1,2)
625: .DE
626: In the above case, the only comma which is an item delimiter is the
627: one before "a(1,2)".
628: .I Prncpy
629: is a function designed to parse this type of construct. The call to
630: .I prncpy
631: is as follows:
632: .DF 1 0
633: char *prncpy();
634: char *pt
635: extern char *expr;
636: char to[80];
637: pt = prncpy(to,expr);
638: .DE
639: Assuming that "expr" is a pointer to the beginning of the expression
640: field (see 3.3),
641: .I prncpy
642: takes the following action:
643: .VL 8 5
644: .LI 1)
645: Copies the characters beginning at "expr" to the array "to" up to
646: but not including the first comma
647: which is
648: .B not
649: enclosed in parentheses.
650: If the end of string (NULL byte) occurs before such a comma is found,
651: the copy stops. In either case the string copied into "to" is NULL
652: terminated.
653: .LI 2)
654: Returns a pointer to the comma or NULL byte. In this way the calling routine
655: can use the returned pointer to determine what the delimiter was and if
656: there are more items to be parsed, it has a pointer to the beginning of the
657: next item by bumping the returned pointer by one.
658: .LE
659: .SP
660: .H 3 evalx
661: The inner workings of
662: .I evalx
663: will be described in a separate memorandum by J. P. Hawkins who is
664: the author of that particular function. However, it is of utmost
665: importance to this discussion and therefore will be described here
666: in enough detail to allow readers of this document to use it.
667: .P
668: The call to
669: .I evalx
670: is
671: .DF 1 0
672: char *pointer;
673: double evalx();
674: evalx(pointer);
675: .DE
676: Here, "pointer" must point to the beginning of a NULL or
677: .I keyword
678: terminated expression (See 3.1 note 3).
679: Where in this case,
680: expression is one of the following:
681: .VL 8 5
682: .LI 1)
683: A variable. Such as "a" or "b1" etc.
684: .LI 2)
685: A Subscripted variable. Such as "a(1,1)".
686: .LI 3)
687: Any numeric literal. Such as "10" or "551.325" etc.
688: .LI 4)
689: A function reference. Such as "sin(3.14159)" or "log(10)" etc.
690: .LI 5)
691: A mathematical expression consisting of any or all of the above
692: connected by mathematical operators. Such as
693: .DF 1 0
694: a+a(1,1)*551.325/sin(3.14159)
695: .DE
696: The keywords and their associated ascii codes are:
697: .DF 2 0
698: .B "KEYWORD ASCII CODE(octal)"
699: goto 01
700: go to 02
701: then 03
702: to 04
703: step 05
704: <> 06
705: <= 07
706: =< 10
707: < 11
708: >= 12
709: => 13
710: > 14
711: = 15
712: gosub 16
713: .DE
714: .SP
715: .I Evalx
716: takes the following action:
717: .VL 8 5
718: .LI 1)
719: Evaluates the expression and returns its numeric value in double precision
720: floating point form.
721: .LI 2)
722: Sets a global character pointer "eoexpr" (mnemonic for "end of expression
723: pointer") to point to the character at the end of the expression.
724: .LI 3)
725: Unlike other
726: .I bite
727: functions,
728: "Evalx" can not return a "-1" if an error occurs during the expression
729: evaluation because "-1" is a perfectly valid value. So, if an error occurs
730: during expression evaluation,"evalx" prints an error message, sets the
731: stop flag "stpflg" and returns a value of zero.
732: This will cause program termination when control
733: is returned to the run time moniter.
734: .H 2 "Calling command functions as though they were internal functions".
735: Very often, one of the
736: .I bite
737: commands already does exactly whats needed in the way of expression
738: parsing. For example, consider the instruction
739: .DF 2 0
740: 10 for i=1 to 100
741: .DE
742: The first thing that the
743: .I for
744: function must be concerned with is setting the variable "i" equal to
745: 1. The
746: .I let
747: command already deals with expressions of this nature and the pointer
748: "expr" already points to the "i" at the beginning of the
749: expression field (See 3.3). So one of the first statements
750: in
751: .I for
752: is
753: .DF 2 0
754: let();
755: .DE
756: Since
757: .I let
758: uses
759: .I evalx
760: to evaluate that part of the expression to the right of the "=", and
761: since
762: .I evalx
763: terminates at
764: .I keywords
765: (in this case the "to"), on return from the call to
766: .I let
767: the variable "i" will have been set to 1 and the pointer
768: .I eoexpr
769: will be pointing to the ascii character "004" which is the
770: encoded "to". The
771: .I for
772: function then proceeds to deal with that part of the expression beyond
773: the "to" and of course uses
774: .I evalx
775: again to accomplish that.
776: .H 3 Example.
777: The
778: .I on
779: function is fairly short and provides a good sample of the features
780: discussed thus far. The source code is as follows:
781: .DF 1 0
782: /* routine to handle on goto */
783: /* R.B. Drake 4/26/79 */
784: #include <bas.h>
785: extern int stpflg;
786: double evalx();
787: char *prncpy();
788: extern char *expr,*eoexpr;
789: on()
790: {
791: int i,j;
792: char buffer[80];
793: char c;
794: if((j = evalx(expr)) <= 0)
795: {
796: error(inst.linno,38);
797: return(-1);
798: }
799: /* "stpflg" is set by the interrupt routine "stopl"
800: * when the user hits the "del" or "rubout" key.
801: * When "stpflg" becomes set, the "run" is to be
802: * terminated. It is necessary to test it here because
803: * this routine can remain active for an unlimited number
804: * of instructions and may be called recursively.
805: * i.e. "gosub" does not return until it encounters a
806: * "return" instruction in the program buffer and there
807: * is nothing which prevents the occurrence of other
808: * "gosub's" or other "on" statements within the range
809: * of "gosub".
810: */
811: if(stpflg == 1)
812: return(-1);
813: if((c= *eoexpr++) > '\\003' && c != '\\016')
814: {
815: error(inst.linno,8); /* expression syntax */
816: return(-1);
817: }
818: for(i=0;i<j;i++)
819: {
820: eoexpr = prncpy(buffer,eoexpr);
821: if(*eoexpr++ == '\\0' && i != (j-1))
822: {
823: error(inst.linno,38); /* not enough references*/
824: return(-1);
825: }
826: }
827: expr = buffer;
828: if(c != '\\016')
829: {
830: if(__goto() < 0)
831: return(-1);
832: return(0);
833: }
834: if(gosub() < 0)
835: return(-1);
836: return(0);
837: }
838: .DE
839: Recall that the format of the
840: .I on
841: statement in
842: .I bite
843: is
844: .DF 2 0
845: line# on expr goto line#,line#......,line#
846: or
847: line# on expr gosub line#,line#......,line#
848: .DE
849: Where "expr" will be evaluated and truncated to an integer
850: indicating which of the "line#'s" to go to.
851: .P
852: Referring now to the above source code, note that the very first
853: executable instructions is
854: .DF 2 0
855: if(j = evalx(expr) <= 0)
856: .DE
857: Whatever the form of "expr", (See 3.6.2)
858: .I evalx
859: will return its value as a double precision
860: floating point number. However, since "j" was declared integer,
861: C will automatically truncate it. Of course if it's value is negative,
862: there is an error because negative numbers have no place in this context.
863: .I Evalx
864: will return at the first
865: .I keyword
866: which in this case must be either "goto" or "gosub". This syntax
867: is tested by the statement
868: .DF 2 0
869: if((c= *eoexpr++) > '\\003' && c != '\\016')
870: .DE
871: Where '\\003' is the
872: .I ascii
873: character for "goto" and '\\016' the "gosub" (See 3.6.2).
874: Note that the "++" in the above statement pushes the pointer
875: beyond the
876: .I keyword
877: and makes it point to the beginning of the list of
878: line numbers. Remember that the
879: .I bite
880: editor
881: .I bed
882: strips all blanks and tabs out of the expression field.
883: Therefore, there is no need for any of the command functions
884: to worry about white space.
885: The "for" loop beginning at
886: .DF 2 0
887: for(i=0;i<j;i++)
888: .DE
889: takes care of copying the j'th line# in the list
890: into the array "buffer".
891: This is accomplished
892: by calling
893: .I prncpy
894: (See 3.6.1) "j" times, while making sure that the end of the
895: expression string is not reached before the j'th line number is
896: found. Now, "buffer" contains a character string representing
897: a line number which is to be the object of the "goto" or "gosub".
898: Since
899: .I bite
900: already has functions to handle "goto" and "gosub" all that is
901: necessary is to set the pointer "expr" to point to "buffer"
902: and call the appropriate "go" routine.
903: .I Bite
904: functions don't
905: care whether
906: "expr" points into the program buffer or somewhere else as
907: long as the string it points to has the appropriate syntax.
908: .H 2 "Dealing With Variables" .
909: In the discussion of the "for" command in section 3.7, the issue
910: of variables was avoided by allowing the
911: .I let
912: function to handle them. It is recommended that, where ever possible,
913: that procedure be followed. However, there may well be situations
914: where that is not practical. There are several internal functions
915: which are used for this purpose as follows:
916: .DF 1 0
917: class(from,to)
918: getvar(varnam,value)
919: agetvar(varnam,value)
920: putvar(varnam,value)
921: aputvar(varnam,value)
922: .DE
923: A brief description of the calling protocol and the action of each
924: of these will be given here. The details on how they work will be
925: covered in another memorandum.
926: .H 3 class(from,to)
927: .SP
928: Calling protocol:
929: .SP
930: .DF 1 0
931: char *from; /* pointer to an expression string */
932: char *to; /* pointer to a character array large enough
933: * to hold one element of the expression */
934: class(&from,to);
935: .DE
936: .I Action:
937: .VL 8 5
938: .LI 1)
939: Copies one element of the expression into the array "to" and
940: NULL terminates the string. Where an expression element is
941: a mathematical operator,
942: a numeric literal, a variable name, an array name or a function name.
943: (See 3.6.2).
944: .LI 2)
945: Bumps the pointer "from", leaving it pointing to the first character
946: of the next expression element.
947: .LI 3)
948: Returns an integer, which is a code telling the calling routine
949: what kind of element was copied. These integers are defined as
950: manifest constants in the header file
951: .I bas.h
952: as follows:
953: .DF 1 0
954: #define OPCLASS 1 /* operator field ^ * / + - ( */
955: #define NMCLASS 2 /* numeric field */
956: #define VRCLASS 3 /* variable name */
957: #define VACLASS 5 /* variable array name */
958: #define FNCLASS 6 /* function name */
959: .DE
960: .H 3 "Getvar and Agetvar"
961: .SP
962: Calling protocol for getvar:
963: .SP
964: .nf
965: char *string; /*pointer to a string containing a variable name*/
966: double value; /*place to store the variable's value*/
967: getvar(string,&value); /* call */
968: .SP
969: Calling protocol for agetvar:
970: .SP
971: Same as for getvar except "string" must point to the name of an
972: array variable name.
973: .SP
974: .fi
975: These routines find the address of the data associated with the variable
976: or array name, retrieve the data from the data buffer and store it in
977: the location specified by their second argument.
978: .H 3 "Putvar and aputvar"
979: .SP
980: Calling protocol for putvar:
981: .SP
982: .nf
983: char *string; /* pointer to the name of a variable*/
984: double value; /*data to be stored*/
985: putvar(string,value);
986: .SP
987: Calling protocol for aputvar:
988: .SP
989: Same as for putvar except "string" must point to the name of an
990: array variable.
991: .SP
992: .fi
993: These routines find the address of
994: the storage area in the data buffer where the data associated with the
995: variable name is to go. In the case of
996: .I putvar
997: if no such storage area exists, one will be allocated. In the case
998: of
999: .I aputvar ,
1000: a storage area must have been reserved by a previous "dim" statement
1001: or a fatal error will occur with an appropriate error diagnostic
1002: message.
1003: .H 4 "Example."
1004: Perhaps the most complete example using all of these functions is in
1005: "evalx.c". However, "evalx" is long and complex therefore we will
1006: use the simpler "let" routine which hopefully will demonstrate the
1007: point without clouding it by complexity.
1008: .DF 1 0
1009:
1010: #include <bas.h>
1011: /*
1012: #define skip00() {while(*lexptr == ' ' || *lexptr == '\t') *lexptr++;}
1013: */
1014: #define skip00() {} /* does nothing */
1015:
1016: let()
1017: {
1018: int type; /* field type */
1019: char varnam[40];
1020: float evalx();
1021: float value;
1022: char field[40];
1023: char *lexptr; /* string pointer for let */
1024:
1025: lexptr = expr; /* set text pointer */
1026:
1027: type = class(&lexptr,field); /* get field type */
1028: if((type != VRCLASS) && (type != VACLASS)) /* if not var */
1029: {
1030: error(inst.linno, 3); /* ill var name */
1031: return(-1);
1032: }
1033: strcpy(varnam, field); /* copy variable name */
1034: skip00();
1035: if(*lexptr++ == '\15') /* if code for '=' */
1036: {
1037: value = evalx(lexptr); /* evaluate right side */
1038: switch(type)
1039: {
1040: case VRCLASS: /* regular variable name */
1041: putvar(varnam, value);
1042: return(0);
1043: break;
1044: case VACLASS: /* subscripted var field */
1045: aputvar(varnam, value);
1046: return(0);
1047: break;
1048: default:
1049: break;
1050: }
1051: }
1052: else
1053: {
1054: error(inst.linno, 8); /* missing = */
1055: return(-1);
1056: }
1057: }
1058: .DE
1059: "Let" of course expects to see an expression field of the form
1060: "variable = mathematical expression" (See 3.6.2 for the definition
1061: of these terms). Therefore the first thing it does is make sure that
1062: the expression begins with a variable or array name.
1063: This is done by:
1064: .DF 1 0
1065: type = class(&lexptr,field); /* get field type */
1066: if((type != VRCLASS) && (type != VACLASS)) /* if not var */
1067: {
1068: error(inst.linno, 3); /* ill var name */
1069: return(-1);
1070: }
1071: .DE
1072: After the call to "class", "type" should be equal to either
1073: "VRCLASS" or "VACLASS", "lexptr" should be pointing to an "="
1074: and "field" will contain the null terminated name of the variable
1075: or array. (in the case of an array, the name includes the parenthises
1076: and what's inside them i.e. "a(x,y)" ). The variable or array name
1077: will be needed later so is copied to a new temporary location "varnam"
1078: by:
1079: .DF 1 0
1080: strcpy(varnam, field); /* copy variable name */
1081: .DE
1082: Then a test is made to be sure that "lexptr" is pointing to an "="
1083: and at the same time "lexptr' is bumped so that it will point to the
1084: first character of the mathematical expression by the statement:
1085: .DF 1 0
1086: if(*lexptr++ == '\15') /* if code for '=' */
1087: .DE
1088: If "lexptr" was not pointing to an "=" an error message is issued
1089: and a fatal error indicated by:
1090: .DF 1 0
1091: else
1092: {
1093: error(inst.linno, 8); /* missing = */
1094: return(-1);
1095: }
1096: .DE
1097: But assuming that everything is allright so far, the mathematical expression
1098: is evaluated by:
1099: .DF 1 0
1100: value = evalx(lexptr); /* evaluate right side */
1101: .DE
1102: Now all that remains is to store "value" in the appropriate variable or
1103: array element. This is handled by:
1104: .DF 1 0
1105: switch(type)
1106: {
1107: case VRCLASS: /* regular variable name */
1108: putvar(varnam, value);
1109: return(0);
1110: break;
1111: case VACLASS: /* subscripted var field */
1112: aputvar(varnam, value);
1113: return(0);
1114: break;
1115: }
1116: .DE
1117: Admitedly this example has not used either "getvar" or "agetvar",
1118: but they are so similar to their "put" sisters that such an example
1119: does not seem necessary.
1120: .H 1 "ADDING FUNCTION SUBROUTINES"
1121: Function subroutines differ from commands in that they appear in the
1122: expression field and are replaced during expression evaluation by the
1123: value they return. The syntax is:
1124: .DF 1 0
1125: anyname(arg)
1126: .DE
1127: There is no hard limit as to the number of characters that can be
1128: in the name but make sure it can't be mistaken for an array name
1129: (i.e. it must be at least two characters long and
1130: the second character must
1131: not be
1132: a digit).
1133: "Arg" may be nothing or any expression acceptable to "evalx" (see 3.6.2).
1134: Only one argument is permitted. Adding a function subroutine to
1135: .I bite
1136: is very similar to adding a command except that the function declaration
1137: and dispatch table are in "evalx.c", just preceeding the routine called
1138: "mathcall". "Mathcall" is the routine which actually handles the calling
1139: of function subroutines. To add a function subroutine, first write your
1140: function in "c" being sure to declare it as a "float" function and if
1141: there is to be an argument, the argument must also be declared as float.
1142: Then declare its name along with the others (i.e. float exp()) and add
1143: it's name into the dispatch table "mathtbl" (i.e. {"exp", exp},).
1144: .H 2 "Example".
1145: Perhaps the simplest example of a function subroutine, so simple in fact
1146: that it was included right in the "evllx.c" file instead of as a seperate
1147: file, is "int". "Int" simply truncates a floating point number to an integer
1148: value. Since there are no actual integers in
1149: .I bite
1150: the value is still in floating point form but the fractional part of the
1151: number will be zero. "Int" appears as follows:
1152: "Int" appears as follows:
1153: .DF 1 0
1154: float _int(num)
1155: float num;
1156: {
1157: long trunc;
1158: trunc=num;
1159: num=trunc;
1160: return(num);
1161: }
1162: .DE
1163: This routine simply makes use of the automatic type conversions and truncation
1164: provided by the "c" compiler. That is, "num" is truncated and
1165: converted to a long integer
1166: by the statement "trunc=num" and then from that integer back to a floating
1167: value by the statement "num=trunc". It's use in bite is as follows:
1168: .DF 1 0
1169: 20 b = (1+int(1.1234))/2
1170: .DE
1171: After executing the above instruction the value of "b" will be 1.
1172: i.e. (1+1)/2.
1173: .H 1 "Conclusion"
1174: One of the reasons that UNIX is becoming very popular throughout the
1175: computing industry is that it's source code is aviailable and it is
1176: written predominantly in a semi high level language, "C". Users are
1177: no longer stuck with the features that the system designers decided
1178: that they should have. Rather the user is free to add or subtract
1179: whatever seems appropriate for his task.
1180: .I Bite
1181: is no exception, we feel that we have provided a strong foundation for
1182: a good interprative language but make no pretence that we have thought
1183: of everyting.
1184: We invite users to dig into it, improve it and add to it.
1185: The author hopes that this memorandum provides enough information to make
1186: that task easy so that no one will again feel the need to start from
1187: scratch as we did.
1188: .H 1 ACKNOWLEDGEMENTS
1189: .I Bite
1190: is entirely the work of the author and J. P. Hawkins. However, two people
1191: helped us immeasurably by being our "friendly users". They began using
1192: .I bite
1193: when it was only half finished and far from debugged. Many thanks to
1194: Nick Episcopo and Don Jackowski.
1195: Their patience is much appreciated and the evidence of their
1196: suggestions is everywhere.
1197: .CS
1198: .TC
1199: .SG UNIX
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.