Floo: A Glk-Native Scripting Language Version 0.5 Andrew Plotkin Floo is a simple, Glk-native scripting language. It is closely based on the syntax of PostScript, which in turn in based on Forth and other stack-centric languages. Like PostScript, Floo is easy to parse, runs quite efficiently for an interpreted language, and has even less punctuation than Lisp. (Note, however, that Floo is not PostScript. PostScript has built-in graphics and page layout operators. Floo has Glk operators. Floo is also considerably simpler than PostScript in many ways; I didn't try to implement all of PostScript's functionality. And there are other differences here and there, for example string behavior.) For current Floo information, interpreters, documentation, and sample code, see the Floo home page at . 1 A Floo Program 2 What's In Floo 2.1 Integer 2.2 Boolean 2.3 Null 2.4 String 2.5 Name 2.6 Operator 2.7 Mark 2.8 Array 3 A Note on Garbage Collection 4 Execution 5 Errors 6 Operators and Predefined Names 6.1 Predefined Names 6.2 Stack Operators 6.3 Arithmetic and Boolean Operators 6.4 Relational Operators 6.5 Dictionary Operators 6.6 Flow of Control Operators 6.7 Array Operators 6.8 String Operators 6.9 Conversion Operators 6.10 Miscellaneous Operators 7 Glk and Floo 1: A Floo Program A Floo program, or script, is a text file; you edit it with a standard text editor. The first line must begin with the five characters "#FLOO" (upper case required.) This indicates that the program really is a Floo program. The rest of the first line is ignored. Everything after that line is program text. The character "#" begins a comment, and the rest of that line is ignored. Other than that, all whitespace is treated equally -- tabs, spaces, and line breaks -- so you can format your program as you like. Floo does not care what kind of line breaks you use (Mac, Unix, or DOS-style.) 2: What's In Floo The basic objects in Floo are, well, objects: integer objects, string objects, boolean objects, and so on. There are also array objects, which contain other objects. Objects can be stored in two places. First, there is the system dictionary, which keeps objects associated with names, so that you can easily look them up. Second, there is the stack -- a standard last-in-first-out stack of objects. All operators work by manipulating objects on the stack. These are all the kinds of objects that exist: 2.1: Integer A 32-bit signed integer. In a program, an integer is written as a decimal number, or a hexadecimal number with the 0x prefix. Some examples of integers: 0 73 -21 0xFF1C You can use upper or lower case in hexadecimal numbers. 2.2: Boolean True or false. You can write boolean objects as: true false [[Actually, these are not themselves boolean objects, but names which are bound to boolean objects in the system dictionary. Don't worry about this for now.]] 2.3: Null There is exactly one null object. It is used as a placeholder to mean "no object". It is written as: null (Again, this is actually a name bound to the null object in the system dictionary. No matter how many times you write null, you always get the same null object.) 2.4: String An array of 8-bit characters. You write a string in double quotes: "Hello" "One.\nTwo.\n" "Cry \"Havoc...\" and let slip the dogs." As in C, \n means a newline, \" means a double-quote character, and \NNN is any character value (where NNN is a three-digit octal number). You can continue a string over more than one input line by putting a backslash at the end of each line. A Floo string has a maximum length and a current length. The maximum length is fixed; once the string is created, it cannot be made longer. However, the string can be made shorter, or changed, as long as it never exceeds its maximum length. (In fact, the current length of a string is determined by a null byte within it, just as in C. You can bypass the string operators and store a null byte directly into the string, or overwrite a null byte, thus changing its current length. Floo always stores an extra null byte after the maximum string position; this ensures that the current length never exceeds the maximum length.) 2.5: Name A name is an array of characters, like a string, but names are optimized for fast lookup in the system dictionary. Names cannot begin with a digit (or they would be confused with integers.) Some examples of names: add jell_o frog123 /add /jell_o /frog123 A name can be literal or executable. The forward-slash at the beginning determines which. /add and add are the same name, but /add is literal and add is executable. We'll explain the difference momentarily. 2.6: Operator An operator is an internal Floo procedure. There are many operators in the system dictionary, and you execute them to do work. You cannot write an operator directly in your program. You write an executable name, and Floo finds the associated operator in the system dictionary. For example: dup add pop These find (and execute) the three operators which duplicate an item, add two items, and pop an item. The operators themselves have no written form. However, it is convenient to write add when we mean the addition operator, so we will do that throughout this document. 2.7: Mark This is a special kind of object, which (merely by existing) marks a position on the stack. It is mostly used in constructing arrays; see below. In a program, a mark looks like one of these: mark [ (Actually, these two names are bound to an operator which creates a mark object on the stack.) 2.8: Array An array is a list of Floo objects. The length of an array, like the maximum length of a string, is fixed when the array is created. The contents of an array do not have to be all the same type. You write an array like this: [1 2 3] [ "Hello" /there ] [ 1 true [] ] Note that arrays can contain any kind of object, including other arrays. Arrays can be zero-length. Like names, arrays can be literal or executable. The ones shown above are literal. Executable arrays, also called "procedures", look like this: { 1 2 add } { true } Although these two forms look similar, they are actually parsed rather differently. More on this later. 3: A Note on Garbage Collection In a Floo program, you are continually creating objects. Merely writing a number creates an integer object, for example. The Floo interpreter keeps track of these objects, and throws them away if they are no longer accessible from either the stack or the system dictionary. This process is automatic, and you should never have to worry about it. However, the current version of Floo uses an extremely stupid reference-counting system. Everyone knows that reference-counting fails when there are circular references. So if, for example, you insert an array into itself -- or create two arrays that contain each other -- those objects will never be deallocated. Fortunately, this is not something you will generally want to do. If you must, it's best to untangle the knot by overwriting the array with null objects before you stop using it. 4: Execution Your program is nothing but a list of objects. The Floo interpreter reads them in and executes them, one at a time. Here's what happens when an object is executed: Literal objects -- including integers, booleans, strings, nulls, marks, literal names, and literal arrays -- are simply pushed into the stack. If your program looks like: 1 2 "Hello" ...then Floo will push the integer 1 on the stack, then the integer 2, then the string "Hello", and then exit. When an executable name is executed, Floo looks it up in the system dictionary, and executes whatever it finds. If your program looks like 1 2 add ...then Floo will push 1, then push 2, and then look up the name add. This is bound to the addition operator, so Floo executes that. When an operator, such as the addition operator, is executed, it performs whatever function it was meant to do. In the case of add, this pops two numbers off the stack, adds them, and pushes the result back on the stack. So the program shown above will end with the integer 3 on the stack, and nothing else. When a procedure (executable array) is executed, Floo goes through it and executes the objects inside it, in order. Consider the following program: /increment { 1 add } def 5 increment The first line pushes the literal name increment on the stack. The second line pushes the executable array { 1 add }. The third line executes the def operator, which binds a name to an object in the system dictionary; in this case, the name increment is bound to the executable array { 1 add }. Then 5 is pushed. Finally, the executable name increment is executed. This means that Floo looks up increment in the system dictionary, finds { 1 add }, and executes that -- which means pushing 1, and then executing add. The 1 and the 5 are popped off, and 6 pushed back on. Got it? That's all that happens in a Floo program. There are some details. For example, in the second line of that program, Floo encounters an executable array -- but it's pushed on the stack, instead of actually being executed. This is a special case. Executable arrays are not executed when they are read in, only through other means such as system-dictionary lookup. (Without this exception, it would be impossible to define procedures.) Also note that the 1 and add in the procedure are *also* not executed when they are encountered. Objects in braces are not executed; they are just wrapped up in the procedure which is created. This is *not* true of literal arrays. You may recall that [ is an operator which pushes a mark object on the stack. This is in fact how arrays are created. In the program [ 1 2 3 ] ...first a mark is pushed, then 1, then 2, then 3. Then the ] operator is executed. This pops objects off the stack until a mark is reached, and then assembles them into an array, and pushes the array back on the stack. There is nothing special about the [; it does not prevent execution of objects. The program [ 1 2 3 add ] ...produces the array [1 5], because when the ] operator is executed, the stack contains a mark, then 1, then 5. 5: Errors Things can screw up. Since there is no compilation in Floo, errors always occur at run-time. When an error occurs, it is handled in a standard way, which you can override if necessary. Every error has a name. These are the errors currently defined in Floo: * stackunderflow: An operator attempted to pop more operands than were available on the stack. * typecheck: An operand on the stack was the wrong type. * rangecheck: A value was out of range (for example, beyond the bounds of an array). * undefined: A name being looked up had no definition in the system dictionary. * undefinedresult: The result of an operation was undefined (for example, division by zero). * unmatchedmark: An array being constructed failed to find a mark on the stack (that is, there was a ] with no matching [). When an error occurs, Floo first restores the stack to the state it was in before the operator was called. Floo then writes the name of the error (a literal name) into a special dictionary called errinfo, bound to the name errorname. It also writes the offending operator bound to the name command. The error name is then looked up in a second special fictionary called errdict. The result will be a procedure; Floo executes this. That's all. Unless the procedure modifies the flow of control, Floo will continue executing the program after the offending operator. However, all the error-handling procedures in errdict generally *do* modify the flow of control. By default, they are all the same, and look like this: { handleerror stop } handleerror is an operator that displays information about the error, using the data that was squirreled away in errinfo. stop causes the program to stop. You may redefine any of the error-handlers in errdict. You may also redefine handleerror to change the way error information is reported. The standard handleerror operator opens a new Glk window to display its information. If you start messing with the error-handling system, be aware of this. [[Floo's error-handling procedure is vaguely related to that of PostScript, but it is not the same at all. Don't make assumptions.]] 6: Operators and Predefined Names Floo has a big pile of useful operators. Most are modelled after PostScript operators, although the syntax is not always exactly the same. These sections give examples in the form *items* operator => *result* ...showing the state of the stack before and after the operator executes. 6.1: Predefined Names These are not operators per se; they are names in the system dictionary which are bound directly to certain objects. true and false are bound to the boolean objects *true* and *false*. null is bound to the null object. 6.2: Stack Operators dup duplicates the top item on the stack. *obj* dup => *obj* *obj* exch exchanges the top two items on the stack. *obj1* *obj2* exch => *obj2* *obj1* pop removes the top item on the stack. *obj* pop => index makes a copy of an item a given distance down the stack. The original item, and the items above it, remain on the stack unchanged. *obj0* 0 index => *obj0* *obj0* *obj2* *obj1* *obj0* 2 index => *obj2* *obj1* *obj0* *obj2* copy makes a copy of *count* items at the top of the stack. The original items remain on the stack below the new copies. *obj1* *obj2* *...* *objn* *count* copy => *obj1* *obj2* *...* *objn* *obj1* *obj2* *...* *objn* 0 1 2 3 4 2 copy => 0 1 2 3 4 3 4 0 1 2 3 4 4 copy => 0 1 2 3 4 1 2 3 4 roll rotates the order of the top *count* objects on the stack. If *shift* is positive, the objects are shifted upwards, with the top ones moving around to the gaps below. If *shift* is positive, the objects are shifted downwards. *obj1* *obj2* *...* *objn* *count* *shift* copy => *obj(1-shift)%n* *obj(2-shift)%n* *...* *obj(n-shift)%n* 0 1 2 3 4 4 2 roll => 0 3 4 1 2 0 1 2 3 4 5 3 roll => 2 3 4 0 1 0 1 2 3 4 4 -1 roll => 0 2 3 4 1 6.3: Arithmetic and Boolean Operators add adds the top two items on the stack, and puts the sum back on the stack. *num* *num* add => *num* 3 7 add => 10 sub subtracts the top item on the stack from the second item. *num* *num* sub => *num* 3 7 add => -4 mul multiplies the top two items on the stack. *num* *num* mul => *num* 3 7 mul => 21 idiv divides the second item on the stack by the top item. This is integer division, and the result is rounded towards zero. *num* *num* idiv => *num* 7 3 idiv => 2 -7 3 idiv => -2 3 7 idiv => 0 mod takes the remainder of dividing the top two items on the stack. The sign of the result is the same as the sign of the first operand. Note that (a idiv b) * b + (a mod b) is always equal to a. *num* *num* mod => *num* 7 3 mod => 1 -7 3 mod => -1 7 -3 mod => 1 abs takes the absolute value of the top item on the stack. *num* abs => *num* 7 abs => 7 -7 abs => 7 neg takes the negative of the top item on the stack. *num* neg => *num* 7 neg => -7 -7 neg => 7 bitshift performs an unsigned (logical) shift of the second item by the top item. *num* *num* bitshift => *num* 7 1 bitshift => 14 7 -1 bitshift => 3 not performs a logical or bitwise *not* of the top item on the stack. *bool* not => *bool* *num* not => *num* true not => false false not => true -2 not => 1 and performs a logical or bitwise *and* of the top two items on the stack. *bool* *bool* and => *bool* *num* *num* and => *num* true true and => true true false and => false false true and => false false false and => false 5 6 and => 4 or performs a logical or bitwise *or* of the top two items on the stack. *bool* *bool* or => *bool* *num* *num* or => *num* true true or => true true false or => true false true or => true false false or => false 5 6 or => 7 xor performs a logical or bitwise *xor* of the top two items on the stack. *bool* *bool* xor => *bool* *num* *num* xor => *num* true true xor => false true false xor => true false true xor => true false false xor => false 5 6 xor => 3 6.4: Relational Operators eq determines if the top two items on the stack are equal. It puts true on the stack if they are, and false if they are not. Boolean and integer objects are equal if they have the same value. Names are equal if they look the same. All marks are equal, and all nulls are equal. Strings are equal if they have the same *current* length and are identical through that length. (Differences after the first null byte are ignored.) Arrays are equal only if they are the very same object; arrays are *not* compared element by element. Objects of different types are never equal. *obj* *obj* eq => *bool* 4 5 eq => false false false eq => true "too" "tooth" eq => false [ 1 2 ] dup eq => true [ 1 ] [ 1 ] eq => false /this /this eq => true ne determines if the top two items on the stack are not equal. It uses the same rules as eq. *obj* *obj* ne => *bool* lt determines if the second item on the stack is less than the top item. *num* *num* lt => *bool* 3 5 lt => true 3 -5 lt => false le determines if the second item on the stack is less than or equal to the top item. *num* *num* le => *bool* gt determines if the second item on the stack is greater than the top item. *num* *num* gt => *bool* ge determines if the second item on the stack is greater than or equal to the top item. *num* *num* ge => *bool* 6.5: Dictionary Operators def binds an item to a literal name in the system dictionary. You may then use the executable form of the name to look up and execute the item. *name* *obj* def => /seven 7 def seven 1 add => 8 /square { dup mul } def 3 square => 9 load looks up a literal name in the system dictionary, and puts what it finds on the stack. (The item it finds is not executed.) If the name is not defined, an error occurs. *name* load => *obj* /seven 7 def /square { dup mul } def seven load => 7 square load => { dup mul } bind looks through a procedure. For each executable name it finds, it looks up what that name is bound to; if it is an operator, the name is replaced with the operator it represents. (Names bound to other objects are left unchanged.) Running bind on a procedure is useful for two reasons. First, the procedure will run faster, since it can execute operators directly, instead of having to look up their names in the system dictionary. Second, the procedure cannot become confused if the names of standard operators are later redefined. *proc* bind => *proc* { 1 2 add } bind => { 1 2 ** } /increment { 1 add } bind def /add { sub } def 5 increment => 6 6.6: Flow of Control Operators exec executes the top item, in the usual manner. That is, executable names are looked up and executed, procedures are executed, and literal items are pushed back on the stack unchanged. *obj* exec => *...* { 1 2 add } exec => 3 27 exec => 27 1 2 /add load exec => 3 if executes one item, but only if a boolean is true. If the boolean is false, the item is discarded without effect. if removes both items from the stack before executing, and does not itself push anything back; but the procedure it executes may affect the stack. *bool* *obj* if => *...* true { 1 3 add } if => 4 false { 1 3 add } if => ifelse is like if, but it executes one of two items and discards the other. *bool* *obj1* *obj2* ifelse => *...* true { 1 3 add } { "hello" } ifelse => 4 false { 1 3 add } { "hello" } ifelse => "hello" loop executes an item repeatedly, until exit or stop is executed. If the item does not contain one of these operators, this will probably cause an infinite loop or stack overflow. *obj* loop => *...* { "hello" exit } loop => "hello" 1 loop => *...stack overflow with 1's* repeat executes an item a fixed number of times, unless exit or stop is executed first. *num* *obj* repeat => *...* 5 1 repeat => 1 1 1 1 1 4 { "hello" exit } repeat => "hello" for executes an item repeatedly. A number is pushed on the stack before every repetition, and the number is incremented each time. The loop ends when the number exceeds a given limit. (The increment may be negative, in which case "exceeds" means in the negative direction.) (If the increment is zero, the loop will never terminate.) In general, the procedure you repeat will use the pushed number for some purpose. If it does not, the numbers will accumulate on the stack. Again, the exit and stop operators will exit the loop prematurely. *initial* *increment* *final* *obj* for => *...* 1 1 5 { } for => 1 2 3 4 5 6 -2 1 { } for => 6 4 2 0 1 1 5 { add } for => 15 exit terminates a loop immediately. (If there are several nested loops in progress, it terminates the innermost one.) Execution continues normally after the loop's exit point. If there is no loop in progress, the program terminates. exit => 1 1 5 { dup eq 3 { exit } if } for => 1 2 3 continue terminates this repetition of the innermost loop. Execution continues with the next repetition, or after the loop's exit point if this was to be the last repetition. If there is no loop in progress, the program (counterintuitively) terminates. continue => 4 { "one" continue "two" } repeat => "one" "one" "one" "one" stop terminates the entire program. stop => 6.7: Array Operators Note that some of these operators are polymorphic, and can be used on both arrays and strings. See section 6.8, "String Operators". array creates an array of a given length, filled with null objects. (It is legal to create an array of zero length.) *num* array => *array* 3 array => [ null null null ] mark pushes a mark object onto the stack. [ is bound to the same operator. mark => *mark* [ => *mark* ] pulls items from the stack until it encounters a mark object. It discards the mark, and creates an array containing the other items (topmost item at the end.) *mark* *obj0* *obj1* *...* *objn* ] => *array* [ 1 2 "hey" ] => *array containing 1, 2, and "hey"* length determines the length of an array (or a procedure, which is an executable array.) *array* length => *num* [ 1 [ 2 3 ] "hey" ] length => 3 get extracts a single element from an array. Arrays are indexed from 0 to n-1, where n is the length of the array. *array* *num* get => *obj* [ 1 [ 2 3 ] "hey" ] 2 get => "hey" put puts a single element into an array, discarding the element that was there originally. Arrays are indexed from 0 to n-1, where n is the length of the array. *array* *num* *obj* put => /arr [ 1 [ 2 3 ] "hey" ] def arr 2 true put arr => [ 1 [ 2 3 ] true ] getinterval creates a new array containing a subsequence of a given array. The new array consists of *length* elements starting at *start*. *start* and *start+length* must be numbers in the range 0 to n, where n is the length of the array. (Note that it is legal to get a zero-length subsequence beginning at n.) *array* *start* *length* getinterval => *subarray* [ 1 [ 2 3 ] "hey" ] 1 2 getinterval => [ [ 2 3 ] "hey" ] [ "a" "b" "c" ] 2 1 getinterval => [ "c" ] putinterval replaces a subsequence of one array with the elements from another. The replaced elements are discarded. All the elements in *array2* are written into *array1*, starting at position *start* -- this must not run outside the bounds of *array1*. *array1* *start* *array2* putinterval => /arr [ 1 [ 2 3 ] "hey" ] def arr 1 [ "wob" 31 ] putinterval arr => [ 1 "wob" 31 ] aload extracts all the elements of an array onto the stack, last element on top. Then it pushes the array back onto the stack, on top of everything else. *array* aload => *obj0* *obj1* *...* *objn-1* *array* [ 1 2 3 ] aload => 1 2 3 [ 1 2 3 ] astore takes an array, and fills it by pulling enough objects from the stack to write into every position. Then it pushes the array back onto the stack. *obj0* *obj1* *...* *objn-1* *array* astore => *array* 2 "foo" /thing [ 1 2 3 ] astore => [ 2 "foo" /thing ] 1 2 3 3 array astore => [ 1 2 3 ] 6.8: String Operators Remember that Floo strings are somewhat chimeric. A string is basically a fixed-length array of characters, and any character can occur at any position, including null characters. However, many operators deal with the "current" length of the string, which is a C-style string -- the portion up to (but not including) the first null byte. (And there is an immutable null byte after the end of the array, to ensure that the current length never exceeds the maximum length.) There are also operators that write a C-style string into a string object. These can write as many characters as source string contains, but they also write a null byte after that, so that the destination's current length is set. (Unless the source's current length is equal to the destination's maximum length. In that case, the null byte would fall outside the destination; but that position is the destination's immutable terminal null, so it's already taken care of.) Some of these operators are polymorphic, and can be used on both arrays and strings. See section 6.7, "Array Operators". string creates a string of a given length, filled with null bytes. (The maximum length is therefore whatever you specify, and the current length is zero.) (It is legal to create a string of maximum length zero.) *num* string => *string* 16 string => "" length determines the *maximum* length of a string. *string* length => *num* "vzefh" length => 5 strlen determines the *current* length of a string. *string* strlen => *num* "vzefh" strlen => 5 strcat concatenates one string onto another. The first operand is put back onto the stack, containing the (current) values of both strings together. It must have a maximum length large enough for this. [[If the strings may be of any length, you may want to allocate a new string for the concatenation, as shown below.]] *string1* *string2* strcat => *string1* 12 string "hello" strcat => "hello" 12 string "hey" strcat "ho" strcat => "heyho" /newstrcat { exch 2 copy strlen exch strlen add string exch strcat exch strcat } def "wrong" "foot" newstrcat => "wrongfoot" get extracts a single character from a string. The result will be between 0 and 255. The position you give must be between 0 and n, where n is the string's maximum length. If you specify n, the result will always be zero -- the string's immutable terminal null. *string* *num* get => *num* "ABC" 1 get => 66 "ABC" 3 get => 0 put puts a single character into a string. The position you give must be between 0 and n-1, where n is the string's maximum length. (As a special case, you may legally write a zero value at position n, which of course has no effect.) *string* *num* *byte* put => /str "ABCDE" def str 2 69 put str => "ABEDE" str 2 0 put str => "AB" str 2 67 put str => "ABCDE" getinterval creates a new string containing a subsequence of a given string. The new string has a maximum length of *length*, and consists of *length* characters starting at *start*. *start* and *start+length* must be numbers in the range 0 to n, where n is the length of the array. (Note that it is legal to get a zero-length subsequence beginning at n.) *string* *start* *length* getinterval => *substring* "ABC" 1 2 getinterval => "BC" "frogs" 2 1 getinterval => "o" putinterval replaces a subsequence of one string with the characters from another. All the characters in *array2* are written into *array1* -- the maximum length, not just the current length, but excluding the terminal null. They are written starting at position *start*; this must not run outside the bounds of *array1*. *string1* *start* *string2* putinterval => /str "ABCDE" def str 1 "xyz" putinterval str => "AxyzE" 6.9: Conversion Operators cvs writes a text representation of an object into a given string. If the text representation is longer than the maximum length of the string, an error occurs. The string is then pushed back on the stack. Numbers are represented as signed decimal values; booleans are "true" or "false". A string appears as its current length, and a name appears as it was defined. All other objects appear as the string "--nostringval--". *obj* *string* cvs => *string* 45 "ABCDE" cvs => "45" /add 16 string cvs => "add" cvx converts a literal name or array to an executable one. Other types of objects are not affected. *obj* cvx => *obj* [ add 1 2 ] cvx => { add 1 2 } /add cvx => add cvlit converts an executable name or array to a literal one. Other types of objects are not affected. *obj* cvlit => *obj* { add 1 2 } cvlit => [ add 1 2 ] /add cvx cvlit => /add cvn converts a string to a literal name. The name will contain the same text as the (current value of the) string. Note that you can use this operator to create name objects like " " or "1X2", which cannot be entered directly in a Floo program. *string* cvn => *name* "add" cvn => /add 6.10: Miscellaneous Operators echo takes the top object from the stack and writes it to the current Glk output stream. This output is somewhat smarter than cvs. It writes arrays and procedures as lists of objects in brackets or braces, distinguishes executable names from literal names by putting slashes before the latter, and generally formats its output to be readable by humans. *obj* echo => echostack writes everything on the stack to the current Glk output stream (top object rightmost.) The stack is not changed. *...* echostack => *...* handleerror displays a batch of useful information about the most recent error. It draws this from the special dictionary errinfo, which is filled in by Floo when an error occurs. handleerror first opens a new Glk text-buffer window (three lines high at the bottom of the screen, or filling the screen if there are no windows in existence yet). It then prints out the error name, the object whose execution caused the error, and the (current) contents of the stack. handleerror => glk invokes a Glk function, using the dynamic linking mechanism of Glk. The Floo language is not hardwired with a set of Glk functions. Instead, the library exports its capabilities dynamically, and Floo makes them available to the user. *num* determines which function to call. The number and nature of the operands depend on the function. See section 7, "Glk and Floo". *...* *num* glk => *...* 7: Glk and Floo Glk is the I/O layer through which Floo communicates with the universe. Glk handles *all* Floo input and output. A Floo program should usually start with Glk calls to open a text window; if it does not, the user will not be able to see anything happen. All Glk activity ultimately takes place through the glk operator. However, for convenience, the Floo interpreter queries the Glk library about its capabilities, and creates a set of definitions which the program can use. For example, the glk_put_string() call is assigned function number 0x0082 (decimal 130). You can invoke this by writing: "Hello.\n" 0x0082 glk However, you can also write: "Hello.\n" glk_put_string glk_put_string is a procedure, not an operator; it is defined just as if you had written: /glk_put_string { 0x0082 glk } bind def All Glk functions available in the library will have such procedures defined. In addition, numeric constants (which are given as macro definitions in the C header file glk.h) will be available in the system dictionary as integer objects. So, for example, you can open a text-buffer window and make it the current output stream with the following code: 0 0 0 wintype_TextBuffer 0 glk_window_open /mainwin exch def mainwin glk_set_window The operands of Glk functions are easy to understand for functions that take only integers and strings. Opaque Glk objects, such as windows and streams, are represented in Floo as integer identifiers; so these are easy to handle as well. Matters become more confused when we consider functions that take arrays and pointers. Briefly, all Glk pointer arguments are classified as pass-in, pass-out, or in-out parameters. In addition, NULL may or may not be a legitimate value for any pointer. When you call a Glk function in Floo, the glk operator looks up the function's argument list, checks the stack to make sure the operands are valid, calls the function, and then puts the appropriate operands back on the stack. * Pass-in non-NULL arguments must be given as a value on the stack. * Pass-in NULL-ok arguments may be given as either a value or a null object. * Pass-out non-NULL arguments should not be given at all; the function will create a value and leave it on the stack. * Pass-out NULL-ok arguments must be given as a *boolean* value. This indicates whether you want the function to pass a pointer and leave the resulting value on the stack, or pass NULL and return nothing. * In-out non-NULL arguments must be given as a value on the stack; an updated value will be left on the stack after the function call. * In-out NULL-ok arguments may be given as either a value or a null object. If a value is given, an updated value will be left on the stack; if null, nothing will be returned. At the present time, string arguments to Glk are always pass-in non-NULL parameters. When you pass a string object, the current value of the string is passed to the function. Arrays, however, are more complicated. A single Floo array represents *two* Glk function arguments, an array-of-integer and an array length. Similarly, a Floo string represents two Glk function arguments, an array-of-char and an array length. Array arguments *also* follow the pass-in-out-null rules above -- except that pass-out arguments should be given as arrays or null objects, like pass-in arguments. We understand that this is horribly confusing. Therefore, we will list all the Glk calls in Glk API 0.5, and show how they are called in Floo. [[Remember, as Glk is enhanced, more calls will be available *even in this version of Floo*, as long as the Floo interpreter is linked with the new Glk library. But this should get you started.]] glk_exit => glk_tick => *num* *num* glk_gestalt => *num* *winid* *bool* glk_window_iterate => (*num*) *winid* *winid* glk_window_get_rock => *num* glk_window_get_root => *winid* *winid* *num* *num* *num* *num* glk_window_open => *winid* *winid* *bool* glk_window_close => ([ *num* *num* ]) *winid* *bool* *bool* glk_window_get_size => (*num*) (*num*) *winid* *num* *num* *winid* glk_window_set_arrangement => *winid* *bool* *bool* *bool* glk_window_get_arrangement => (*num*) (*num*) (*winid*) *winid* glk_window_get_type => *num* *winid* glk_window_get_parent => *winid* *winid* glk_window_get_sibling => *winid* *winid* glk_window_clear => *winid* *num* *num* glk_window_move_cursor => *winid* glk_window_get_stream => *strid* *winid* *strid* glk_window_set_echo_stream => *winid* glk_window_get_echo_stream => *strid* *winid* glk_set_window => *strid* *bool* glk_stream_iterate => (*num*) *strid* *strid* glk_stream_get_rock => *frefid* *num* *num* glk_stream_open_file => *strid* *string* *num* *num* glk_stream_open_memory => *strid* *strid* *bool* glk_stream_close => ([ *num* *num* ]) *strid* *num* *num* glk_stream_set_position => *strid* glk_stream_get_position => *num* *strid* glk_stream_set_current => glk_stream_get_current => *strid* *num* *num* glk_fileref_create_temp => *frefid* *num* *string* *num* glk_fileref_create_by_name => *frefid* *num* *num* *num* glk_fileref_create_by_prompt => *frefid* *frefid* glk_fileref_destroy => *frefid* *bool* glk_fileref_iterate => (*num*) *frefid* *frefid* glk_fileref_get_rock => *frefid* glk_fileref_delete_file => *frefid* glk_fileref_does_file_exist => *num* *char* glk_put_char => *strid* *char* glk_put_char_stream => *string* glk_put_string => *strid* *string* glk_put_string_stream => *string* glk_put_buffer => *strid* *string* glk_put_buffer_stream => *num* glk_set_style => *strid* *num* glk_set_style_stream => *num* glk_get_char_stream => *num* *strid* *string* glk_get_line_stream => *num* *strid* *string* glk_get_buffer_stream => *num* *char* glk_char_to_lower => *char* *char* glk_char_to_upper => *char* *num* *num* *num* *num* glk_stylehint_set => *num* *num* *num* glk_stylehint_clear => *winid* *num* *num* glk_style_distinguish => *num* *winid* *num* *num* *bool* glk_style_measure => (*num*) *num* glk_select => [ *num* *winid* *num* *num* ] glk_select_poll => [ *num* *winid* *num* *num* ] *winid* *string* *num* glk_request_line_event => *winid* glk_cancel_line_event *bool* => ([ *num* *winid* *num* *num* ]) *winid* glk_request_char_event => *winid* glk_cancel_char_event => *winid* glk_request_mouse_event => *winid* glk_cancel_mouse_event => *num* glk_request_timer_events => Note that there are two Glk functions which are not listed. glk_gestalt_ext() is not yet available, because I haven't figured out what to do with its annoying overloaded third argument. And glk_set_interrupt_handler() is not available because callbacks are ugly. The Glk dynamic dispatch mechanism does not try to handle them. If an interpreter wants to support them (which Floo does not), it will have to pass its own handler to glk_set_interrupt_handler() and have that take care of things.