Annotation of researchv10no/cmd/basic/bite/doc/bite-inside.m, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.