| Floo: A Glk-Native Scripting Language | |
| Version 0.5 | |
| Andrew Plotkin <erkyrath@netcom.com> | |
| 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 <http://www.eblong.com/zarf/glk/floo/>. | |
| 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 *<operator:add>* } | |
| /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. | |
Xet Storage Details
- Size:
- 39.4 kB
- Xet hash:
- 049e69605049bd918d605875d6e4487bd370d0fd07cab30ee8e36fd58b9bf7f6
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.