Annotation of researchv10no/cmd/basic/bite/doc/bite-inside.m, revision 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.