JaM Introduction This document is a brief description of a Mesa based interactive programming environment called "JaM". In this document three general topics will be discussed: What JaM is; what applications lend themselves to the use of JaM; and how JaM interacts and is used with Mesa. Basic Operation To answer the question: "What is JaM?", several properties of the system will be discussed. 1. JaM implements a basic stack oriented, virtual machine. This machine operates on entities called "objects" which are found on one or more of the machine's three stacks. In a common sense sort of way JaM can be thought of as an extremely flexible programmable Hewlett Packard type calculator that can handle a wide variety of objects in a powerful way. 2. An integral part of the JaM machine is a virtual memory system that has addressing capacity of 224 words. This virtural memory is used to store various definitions and objects created by the user. 3. An interactive user interface to the JaM machine is provided. The interface is implemented as a language having a simple syntax. This language is interpreted by a scanner and transformed into a sequence of objects that are, in turn, executed by the JaM machine. (1792)\f2 1b6f4 3f2 3f3 12B1f2 279f3b16f2 1B555o4 3o0 The JaM machine basically consists of its virtual memory, three stacks, and the instruction set. The three stacks are: The operand stack, the dictionary (or context) stack, and the execution stack. Under most circumstances the user is concerned only with the operand stack. The description of the dictionary stack can be put off until later, although the understanding of its function and use is important. The function of the execution stack need not be of direct concern to the user. However it is required for the internal execution control of the machine. When the JaM machine is in its initial state, the operand stack is empty, the dictionary stack contains one dictionary called the system dictionary, and the machine is executing the keyboard stream (waiting for the user to type something.) As the user types, the characters are buffered until a carraige return is typed. At this point the line is passed to the JaM scanner to be broken into tokens. The tokens are converted into objects and executed in the order they are delivered to the JaM machine. To understand what happens when the user types it is important to understand what the scanner recognizes, and what objects are generated for execution. There are three kinds of things recognized by the scanner and converted into objects. These are: numbers, strings, and identifiers. Numbers are of three types: integers, reals, and long integers.\f2 23 -465 --Examples of integers. -23.4 1. 0.00034 --Examples of reals. -123456 and 1000000 --Examples of long integers. l4268\f2 1f5 9f0 1f2 28f5 19f2 22f5 19f2 Strings are enclosed in balanced parentheses. For example:\f2 (Hello. How are you) (strings may consist of more than one line of information) (strings may (contain nested (balanced parentheses))) (strings may contain all manner of garbage @#$$$%%~~~&&**~%$$1233;;; except for unbalanced parens) (Strings may contain as many as 32767 characters) l4268\f2 1f5 Identifiers consist of any string of characters not containing parentheses, spaces, commas, or carraige returns. For example:\f2 .add .print abc $$$ 123a abc.111 -.aaa l4268\f2 1f5 39f0 are all examples of valid identifiers. When the scanner encounters something that it recognizes, it converts it into an object. In the case of numbers, the scanner converts the number into: a integer object, a long integer object, or a real object based on the syntax or length of the number. In the case of strings, the scanner creates a string object. When the scanner encounters an identifier, it "looks up" the identifier using the dictionary stack as a context (described later), and uses the associated object. As each object is generated by the scanner, it is passed to the JaM machine for execution. To see how this all works, several simple examples will be given.\1f2 Example 1. Suppose I type the following: 123 456 .add .cvs .print 1. The scanner will recognize 123 as a number, convert it to an integer object and pass it to the JaM machine for execution. The JaM machine executes integers by putting them on the operand stack. 2. In a similar way, 456 will be pushed on the operand stack. 3. The scanner will recognize .add as an identifier. ".add" is known in the system dictionary as a command object which, when executed will add two numbers on the operand stack, and leave the resulting integer object on the operand stack. 4. After the .add command is executed, the operand stack will contain the integer object: 579. 5. The scanner will recognize .cvs as an identifier. ".cvs" is known in the system dictionary as a command object which, when executed will convert an object on the operand stack into its string object equivalent. e.g. the integer 579 will be converted to the string "579", and put on the operand stack. 6. In a similar way, .print will be a command object which will print the string found on the operand stack. The result will be that 579 will appear on the screen. Example 2. If I type: (hello there -- how are you) .print then: hello there -- how are you will appear on the screen.l4268\f2 1b10B33f5 24f0 1f2 31f5 3f2 186f5 3f2 69f5 4f2 20f5 4f2 195f5 4f2 73f5 3f2 33f5 4f2 21f5 4f2 174f5 3f2 34f5 3f2 55f5 6f2 108f5 3f2 30b10B14f5 36f0 1f2 8f5 27f2i1I When the JaM machine first starts, the system dictionary contains about sixty definitions. Most of the definitions associate identifiers with command objects. These commands provide the primitive functions of the machine. Included in the primitive operations are commands that allow new definitions to be made into the current dictionary. For example:\f2 Example 3. If I type: (a) 123 .def then the .def command will associate the string object a with the integer object 123 in the current dictionary. With this definition made, if I type: a 1 .add .cvs .print then the scanner will recognize a as an identifier, look up its definition, and execute the integer object 123, which will put the integer on the operand stack. When the rest of the line is executed, then: 124 Will be printed. Example 4. If I type: (a)(hello -- how are you) .def then a is defined to be the string object: hello -- how are you If I now type: a .print then: hello -- how are you is printed.l4268\f2 1b11B13f5 12f2i1I10f5 4f2 42f5 1f2 25f5 3f2 68f5 20f0 1f2 33f5 1f2 74f5 3f2 98f5 3f0 1f2 19b10B14f5 30f0 1f2 6f5 1f2 37f5 20f2 17f5 8f2i1I8f5 20i1f2I Most objects when executed are put on the operand stack. Command objects are an exception. Command objects are treated as verbs by the execution machinery. In a similar way, other objects can be treated as verbs. For example:\f2 Example 5. If I type: (average)(.add 2. .div) .cvx .def then the .cvx command will make the string object: .add 2. .div executable before it is defined as the value of average. Therefore when I type: 123 456 average .cvs .print the identifier average will be defined as the executable string object: .add 2. .div. This string object will not be put on the operand stack, but instead will be passed to the scanner and executed as though it replaced average. The result will be that: 289.5 will be printed.l4268\f2 1b10B14f5 33f2i1I51i1f5I12f0 1f2 49f5 7f2 27f5 27f2i1I16f5 7f2 50f5 12f2 137f5 7f2 29f5 5f2i1I It can be seen from the above simple examples, that the user may assign values to identifiers, or assign procedures (executable objects) to identifiers. This ability to extend the JaM machine is the primary feature that allows complex systems to be built in the JaM environment. To appreciate how this extensibility may be used, a more complete description of the primitive command set, and its relationship to the machine structure will be given. JaM Command Catagories\5f2 281f0 2f2 176b There are roughly nine catagories of JaM commands. These include: Arithmetic Commands The arithmetic commands provide for the basic arithmetic operations between mixed types of numbers. These commands take their operands from the operand stack, and leave the results on the operand stack. Included are: .add : .add => .sub : .sub => .mul : .mul => .div : .div => .neg : .neg => <-n> Boolean and Relational Commands These commands include commands that generate boolean constants, perform relational tests between numeric objects, and perform boolean operations between boolean objects. Included are: .true : .true => <.true> .false : .false => <.false> .eq : .eq => <.true> if n = m <.false> if n # m .gt : .gt => <.true> if n > m <.false> if n <= m .lt : .lt => <.true> if n < m <.false> if n >= m .not : .not => <~b> .and : .and => .or : .or => .xor : .xor => Stack Manipulation Commands These commands provide a set of functions that allow the user to manipulate the operand stack. These functions include facilities to duplicate portions of the stack, rearrange portions of the stack, eliminate portions of the stack, and count the entries on the stack. Included are: .pop : .pop => -- .copy : ... .copy => ... ... .cntstk : | ... .cntstk => .roll : ... .roll => ... ... .dup : .dup => .clrstk : | ... .clrstk => | .exch : .exch => Execution Control Commands This set of commands provides for control of execution. JaM has no "go to" type of command. Instead heavy use is made of "if - else", "looping", and "select" kinds of control mechanisms. Most execution control commands expect objects on the operand stack. These objects are executed as a function of other objects on the stack. For example: The .if command expects a boolean object and any other object on the operand stack. If the boolean equals .true then the other object is executed, otherwise the .if command pops the object from the stack. The execution control commands include: .exec : .exec => -- (executes object x) .if : .if => -- (if b = .true then execute x) .ifelse : .ifelse => -- (if b = .true then execute x else execute y) .rept : .rept => -- (execute x -- i times) .loop : .loop => -- (execute x until a .exit command is executed) .exit : .exit => -- (exit from the current .rept, .loop, .dictforall, .arrayforall commands) .stop : .stop => -- (clear the execution stack.) .singlestep: .singlestep => -- (put execution control into singlestep mode.) .runfree : .runfree => -- (take execution control out of singlestep mode.) .quit : .quit => -- (save virtual memory and exit to the operating system.) Dictionary Related Commands Dictionary objects are general symbol tables that may be used either as data structures or as part of the user's execution context. As we learned earlier, the scanner, when it encounters an identifier, looks up the identifier in the dictionaries on the dictionary stack. What actually happens in this case is quite simple. The unknown identifier is first looked up in the dictionary on the top of the dictionary stack. If the identifier has a value in this dictionary, then the value is returned. If the identifier has no entry in this dictionary, then each dictionary on the dictionary stack is searched until the entry is found. At this point the value associated with the identifier is returned. The user has control over the contents of the dictionary stack via the .begin and .end commands, and therefore the user has control over his execution context. Later we will see how this control may be used. When dictionaries are used as data structures, they are loaded, as objects, onto the operand stack. Various commands can then operate on these objects to store new definitions, or to retrieve definitions found in the dictionaries. Also a command exists for creating new dictionaries. The dictionary related commands are: .dict : .dict => (dictionary with capacity of i entries) .def : .def => -- (associates the value v with the key k in the current dictionary) .del : .del => -- (deletes the key k from the dictionary d) .load : .load => (loads the value associated with k in the current dictionary) .store : .store => -- (finds a definition of k in the current context and replaces that definition with value v. If no definition of k exists then the definition is placed in the current dictionary.) .put : .put => -- (associates value v with key k in dictionary d.) .get : .get => (retrieves the value associated with k in dictionary d.) .known : .known => <.true> if key k is in dictionary d. <.false> if key k is not in dictionary d. .where : .where => <.true> if k is found in some dictionary d. <.false> if k is not found in any dictionary. .clrdict : .clrdict => -- (clears all entries from dictionary d.) .dictforall: .dictforall => -- (puts on the stack, and then executes . This is done for every k,v pair in dictionary d) .begin : .begin => -- (makes d the current dictionary on the dictionary stack.) .end : .end => -- (pops the current dictionary from the dictionary stack.) .sysdict : .sysdict => Array Related Commands Array objects are linear arrays of objects. Commands exist to create arrays, store into arrays, retrieve objects from arrays etc. Most of these commands either expect array objects on the operand stack or return array objects on the operand stack. In addition to their usefulness as data structures, arrays can also be executed directly. In some sense the array form of a procedure corresponds to the compiled form of a procedure in other machines. The array related commands consist of: .array : .array => (new array of length i.) .subarray : .subarray => (a' is the subarray of a starting at position i and with length j.) .aput : .aput => -- (store v in the ith position of a.) .aget : .aget => (get v from the ith position of a.) .aload : .aload => .astore : ... .astore => (store x1 ... xi into array a of length i.) .arrayforall: .dictforall => -- (puts the contents of ai on the stack, and then executes . This is done for every ai pair in array a) Input/Output and Stream Related Commands This catagory of commands deals primarily with string objects and stream objects. There exist primitive JaM commands to create streams, execute streams, read streams, write streams, and distroy streams. These commands include: .print : .print => -- (.prints the string s on the current output stream.) .bytestream: .bytestream => (this command creates a bytestream with the access characteristics represented by . Here = 1 for read, 2 for write, 4 for append -- or the sum of any of these. The created stream type object is left on the operand stack.) .keystream : .keystream => (this command creates a keystream and leaves it on the operand stack.) .killstream: .killstream => (this command kills the given stream.) .writebytes: .writebytes => -- (write bytes in string s appended to stream t.) .loadbcd : .loadbcd => -- (load mesa bcd and start.) Attribute and Conversion Commands These commands allow the user to determine the types of objects and to convert from one object type to another. This command set is not complete as yet, but the currently provided commands include: .type : .type => (deliver the name of the type on top of operand stack. Current types include. .nulltype, .intergertype, .longintegertype, .realtype, .booleantype, .stringtype, .stringlittype, .streamtype, .streamlittype, .arraytype, .arraylittype, .dicttype, .commandtype, .stacktype, .frametype.) .length : .length => (length of: string (in characters); array (in elements); dictionary (in entries).) .cvs : .cvs => (convert to string equivalent.) .cvis : .cvis => (convert into given string space. This command will use s for all number and boolean conversions.) .cvx : .cvx => (convert into executable equivalent.) .cvlit : .cvlit => (convert into literal form -- works for strings, and arrays.) Scanner and String Manipulation Commands As of now, the only command in this group is the .token command which gives the user access to the JaM machine scanner. In the future, commands will be added to this group which will allow for string searching and manipulation.\f2 74b20B1f0 5f2 218f0 141f2b32B1f0 5f2 185f0 282o252 1o0 3o252 1o0 12o252 1o0 6o252 1o0 10o252 1o0 3o252 1o0 11o252 1o0 5o252 1o0 11o252 1o0 3o252 1o0 12o252 1o0 6o252 1o0 3f2b28B1f0 5f2 283f0 34o252 1o0 3o252 1o0 8o252 1o0 16o252 1o0 3o252 1o0 8o252 1o0 3o252 1o0 3o252 1o0 8o252 1o0 16o252 1o0 3o252 1o0 8o252 1o0 27o252 1o0 3o252 1o0 8o252 1o0 19o252 11o0 8o252 1o0 3o252 1o0 6o252 7o0 42o252 1o0 3o252 1o0 8o252 1o0 48f2b27B1f0 5f2 347f0 1f5 3f0 1f2 98f0 1f5 5f0 1f2 48f0 1f5 3f0 1f2 80f0 672f2 1b28B782f5 8f2 3f0 1f5 4f0 1f2 458f0 1339b1f2B1b23B509f0 214o4 2o0 62o4 2o0 52o252 1o0 3o252 1o0 8o252 1o0 29o252 1o0 6o252 1o0 89o252 1o0 66o252 1o0 20f2 1b41B235f0 719f2 1b34B205f0 794f2 1b41B55f5 6f2 .token : .token => (strip first token from given stream or string. Return remainder and token on the stack. If no token, then return only a NullType Object.)