owl Language Reference Manual Antonio Maschio Last update: April 27, 2011 owl version is 0.7.6 Table of Contents ----------------- 1. Welcome 2. Introduction 2.1 A bit of history 2.2 owl is really new 2.3 Conventions used throughout this manual 2.4 Program options 2.5 Parameters 2.6 owl's environment 2.7 Error conditions 3. The language 3.1 Comments 3.2 Numbers 3.2.1 Inputting numbers 3.2.2 Outputting numbers 3.2.3 Truth values 3.2.4 Math Operators 3.2.5 Array operators 3.3 Stack commands 3.4 Storing and fetching commands 3.5 Strings & characters 3.5.1 Strings 3.5.2 Escape chars into strings 3.5.3 Characters 3.5.4 String & character operators 3.6 Functions 3.6.1 Functions commands 3.7 Variables 3.8 Flow control 3.9 Input/Output 3.10 The underscore commands 3.10.1 Correspondence with options 3.10.2 Compilation note 3.11 Limits of calculation 4. Advanced commands 4.1 A Foreword 4.2 Dynamic features 4.2.1 The syntax 4.2.2 Example 4.2.3 Conclusion 4.3 The modules system 4.3.1 Passing data to modules 4.3.2 Passing data from modules 4.3.3 Example 4.3.4 Conclusion 4.4 Including a file 4.4.1 Example 4.5 Recursion 4.5.1 Practical problems in using recursion 5. Debugging owl programs 6. Lectures 6.1 A foreword 6.2 Missing comparison operators 6.3 Missing stack commands 6.4 Missing math functions 6.5 Missing logical operators 6.6 Missing bitwise operators 6.7 The begin-until and the begin-while-repeat cycles 6.8 Notes about symmetry and symbols 7. Installation 7.1 Linux/UNIX (except Mac OS X) 7.2 Mac OS X 7.3 Windows 7.4 Conclusions 8. Bibliography 9. Finally... ****************************************************************************** 1 Welcome ------- Welcome to the owl world; owl, the Obfuscated Weird Language, was inspired by: * the great false programming language by Wouter van Oortmerssen * the great forth programming language by Charles Moore It was also been inspired by bogusforth (or bf for short), another obfuscated stack language of mine, a project that came to an end. Some remarks: * owl, as a UNIX tool, must always be written with lowercase letters. * owl aims to be fast and simple, rich and poor at the same time; after all, what's not present won't break * owl is a stack language, and being stack-based its speed is amazing, even if it's interpreted * owl is coded in standard C, though I added some gcc dialect because of its strength. This could make you change (or delete) all parts that your compiler could not understand Into this project I put all the knowledge in programming gained since I started the bf project (2003), and I enjoyed any moment during its creation. As a final introductory statement, if you are English mother-tongue, forgive me for my inaccurate and simple English. I try to better it every time I open the document source file, but I'm Italian, and the last time I've attended an English course was in 1990 or so... 2 Introduction ------------ owl is an obfuscated stack language based on single-character commands, in the line of the great false programming language, created by Wouter van Oortmerssen in 1993 along with a true compiler. owl has been conceived in the same philosophy (I'm not that clever in computing, thus owl is only an interpreter). In practice, any owl command, instruction or operator is denoted by a single char (e.g. the DUP command to duplicate the Top of Stack is denoted by %); occasionally, two consecutive chars may perform one single action (e.g. _r toggles the rounding option), or one or two characters must be used in conjunction with other variable chars (e.g. A@ returns the value stored in the variable A). Of course **obfuscated** may mean two things: a) the language is hard to read and/or difficult to understand; b) you intentionally want to hide program constructs. For what about point a), consider that any character has a unique and specific function: once you've learned the language, it's not hard to understand any listing (this may be true for any programming language, I guess.) for what about point b), well, owl is an obfuscated language, and hiding is in its nature (because the coding looks like a casual mix of ASCII characters), but if you want to avoid this behavior, put comments and use a beautiful indentation. It's up to you. Programming in owl is not that easy, at start, but once you've gained enough knowledge, it's a very beautiful programming language (maybe just like forth). Trust me. 2.1 A bit of history ---------------- I came across false in mid 2003, and I immediately began imagining how I could design a similar language on my own. My first attempt gave birth to bogusforth, an interpreter that took its name from the fact that it was in practice a fake forth-like stack-based shell interpreter, based on single-char commands. After the first months, in which I mixed coding with designing, bf began to live on its own. I believed it had to have all the forth equivalent commands I could conceive; soon, I started lacking single ASCII chars (they are less than 100, after all), and so I had to invent system commands based on words (like sh, def, append), and distinguish them from simple language key chars. bf had also an inner help file, but keeping it up-to-date began to steal me too much time, more than I could spend on it. Besides, and this is more important, I felt like losing the real nature of a programming language. Basically, bf grew too much! That's why I started developing owl: it's based on principles I want to keep fixed and steady all along the whole development process. Besides, owl is another thing than bf. In fact, the two languages are NOT compatible. 2.2 owl is really new ----------------- I designed owl from scratch, not starting from bf; I considered the needs of the language, and I based the whole project on a compiler point of view. So no interaction, no shell, no system command strings, no need to spy the stack. Instead, I thought of owl as a language for source execution, a sort of shell language, in the purest UNIX point of view (owl may actually be treated as a shell programming language, because it accepts comments beginning with #, and so any owl source may be launched as an executable with any UNIX shell). And more, I started to imagine it powerful but simple, in order to be able (don't know when, yet) to turn it into a real compiler, with lesser effort than that spendable on bf. So, no syntax checking (apart from some basic math control), no error messaging, no floating point math. Just basic bricks. Another point: bf was a project-less project. I started coding before designing, so things went like "Start now and correct later". The compatibility of the language was not guaranteed neither backward nor forward. owl has been designed before coding. The language itself won't change during its development, but if it does, I'll try to guarantee at least backward compatibility. I want to improve routines and develop compiling, not going on developing the language over and over... To summarize, here are some good points: * yes to RPN for math operations and commands; * yes to integer math; * yes to simple input/output through standard channels; * yes to small sets of commands. And then some rejected points: * no to interaction (like in bf) * no to in-line help (it's frustrating adapting it at every change) * no to syntax checking, which slows down processes; stack controls and care on usage are left to the programmer * no to heavy math checking. Few controls and, where possible, predetermined results for impossible calculations. * no to heavy error messaging: only underflow, overflow and few others; * (possibly) no to changes in the language from version to version. This helps in building a complete backward compatibility among the sources (improvements should only be made to build faster and more reliable routines). This revealed harder than I thought (see the paragraph about the include command). 2.3 Conventions used throughout this manual --------------------------------------- In this manual, all elements of the language are exposed with their proper format or symbol. Let's review them: 1. All commands are exposed with their stack notation; the stack notation is a heritage of the forth language and its purpose is to depict the net stack situation before and after the command execution; for instance, if a (fake) command c takes two values from the stack (in order, a boolean and an integer), and returns two other integer values, the stack notation will be: ( n1 b - n2 n3 ) The dash separates the stack before (left) from the stack after (right). b is on Top of Stack, n1 just under. n2 is returned first, n3 then, and stays on Top. If no values are needed by c but some values are returned, the stack notation will be: ( - n1 n2 ) If values are needed but no values are returned, the stack notation will be: ( n1 n2 - ) Finally, if no values are needed nor returned, the stack notation (while unnecessary) will be ( - ) The indexes identify different elements of the same class (e.g. n1 and n2 are both integers and have different values) 2. Classes of elements of the owl language are: | symbol | class ---------------------------------------------------------------------- | n | a signed 64-bits integer | m | an unsigned 32-bits integer quantifying system constants | c | a signed 8-bits integer | u | an unsigned 8-bits integer | b | a boolean flag (a true/false integer) | [f] | a function in the functions buffer | x*n, y*n | any stack configuration | | the input typed by the user after an input request | «stream» | the code following the command into the source | "stream" | a string in PAD | v | a lowercase character for function variables | V | an uppercase character for integer variables | ? | a signed 64-bits integer that may or may not be present +-----------+---------------------------------------------------------- (Table 2.1) 2.4 Program options --------------- owl is launched from console according to the following usage: $ owl [options] filename [parameters] Options are: | option | purpose -----------------+--------------------------------------------------- | --conditions | Print redistribution conditions from GPL3 and exit | -d | Execute program in debug mode | -D | If in debug mode, print var contents | -e | Toggle PAD erasure after initialization of a string | -h, --help | Display this information and exit | -i | Toggle integer division mode | -p '' | Parse enclosed in quotes, execute it, then exit | -r | Toggle rounding mode | -S | If in debug mode, enable single-step execution | -t | Print a timer report in milliseconds after execution | -v, --version | Display version, license and warranty and exit | --warranty | Print warranty conditions from GPL3 and exit +----------------+---------------------------------------------------- (Table 2.2) Notes ----- - a file name must be given (unless the -p option is used), since owl it's not an interactive environment; if the file name has not been given, or if it doesn't exists, a warning is printed; if the file name exists and it's void, another (harmless) notice will appear. If the extension of the program is ".owl" it may be omitted; for example, the call $ owl square will look for file square, then for file square.owl, before giving up. Of course, if you have both square and square.owl in your directory, only the first will be executed! - debug options -d, -D and -S deal with the debugger feature (they are available if owl_DEBUG is defined into source (to disable them, comment the line where it's defined. Anyway, it's an useful option, though simple and minimal; owl_DEBUG is defined by default): the -d option activates the debugger; if the debugger is enabled, the -D option activates the printing of all non-null variables contents; the -S debug function for single step execution is also enabled only in debug mode, and if used, each key-press will advance one command execution; pressing 'S' or 's' will stop the single step running, and the debug will go on as usual; this feature is not available for the Windows version (for the same reasons of the different working of the ( command); see section "Debugging-owl-programs" completely devoted to debug. - the -e option enables clearing PAD after each string loading; by default, the PAD is not cleared, because you may want use it as an array for small values or for composing strings; but if you want to make sure it is cleared before a new string is loaded, to avoid conflict or misbehaving while storing or fetching, use this option, and each string loading will cause PAD to be reset; if you want to clear in specific parts of the code only, you can use the _e underscore command, which performs an instant reset (it clears the integer array also), but without influencing next instructions. - the -i option sets integer division to operate according to the Number Theory; you can also toggle this behavior in the source file by means of the underscore command _i. The integer division, you know, does not exist in the world of integers; in fact, there's no way to obtain an integer result from a / b if b is not a proper factor of a; e.g. 12 / 7 is not possible in the integer world, while 12 /4 is possible by a fortuitous chance. Thus, we have to consider not just a/b but a = bq+r, where q is the integer quotient and r is the remainder (remember your school days?). Now, according to this equation, there are two flavours: a) r is signed (positive or negative) and |r| < |b|; b) r is always positive and r < |b|; case a) leads to the first default type of division, where the quotient is the integer version of the floating point division and the remainder is calculated by means of the equation (e.g. 12/7 = 1 with remainder 5, or -12/7 = -1 with remainder -5); case b) leads to the integer division of the Number Theory, where the remainder is always positive (e.g. 12/7 = 1 with remainder 5, -12/7 = -2 with remainder 2). The two cases differ only when the dividend is negative, and coincide in all other divisions. - the -t option prints a timer report in the form: owl: execution completed in 766 ms. owl: execution completed in 5.852 s. The first (using ms) is printed in case recorded time is less than 1 second. The timing feature is available both for files and for the -p option, but not if an error or an abort instruction (?! or !?) is met across code lines and/or into some function: in this case, an immediate stop will occur, and the timing feature won't be completed. This feature cannot be implemented for Windows. - the -p option executes code on the run, without source files. The string should be enclosed into single quotes, to prevent the shell from interpreting it. At times, it may happen that a string surrounded by double quotes or with no inner spaces and no quotes may be correctly interpreted. It always depends on the shell you are using (for instance, Windows XP Command Prompt needs double quotes, since single quotes raise an error). Some remarks: - the space between p and the string is not required; e.g. $ owl -p'"Hello!"' $ owl -p '"Hello!" are equivalent; - multiple strings may also be given in input, each surrounded by single quotes and separated by nothing or a blank: $ owl -p '1 2 3' '...' is legal, with '1 2 3' the first string pushing three number onto stack, and '...' the second printing the three numbers. This is useful for composing string into script-like commands; see the following, typed from console: $ s1='1 2 3'; s2='...'; owl -p "$s1 $s2" 321 This option may be elegantly used into proper shell scripts (this works for bash, but may work for other shells too) - if the code contains the ' (roll) command, use double quotes for enclosing the string, but inner double quotes now cannot be used. If you should need both the tick and the double quotes into the string, well, write a file! - the -p option now supports the -t option - the -p option itself must be the last option in the command line; for example, the following sequence does not behave as expected: $ owl -pt '"Hello!"' causing owl to interpret the t following p as a program instruction and putting the ASCII value of t onto stack. Even a case like: $ owl -pt'"Hello!"' will be seen as the program t"Hello"; the timing feature won't be, in any case, performed. - as a final remark, if you don't put any string after the -p option, an error message will be issued; I introduced it because the mistake caused the system to issue a bus error message (such messages should always be avoided...). - the -r option toggles the rounding type for results of divisions (additions, multiplications and subtractions always return integer values that need no rounding); by default, the rounding type is set to cut, that is all decimals are cut regardless of their value, so that for instance 4.3 and 4.9 are both cut to 4 and -4.3 and -4.9 are both cut to -4; if you need numbers rounded towards the nearest integer (fractional parts lesser than 0.5 collapse to 0, fractional parts greater than or equal to 0.5 collapse to 1), use this option to set rounding type to promote, so that 4.3 is cut to 4, and 4.9 is promoted to 5, and -4.3 is cut to -4 and -4.9 is promoted back to -5; you can also toggle this behavior in the source file by means of the underscore command _r. - options -h, -v, --conditions and --warranty offer a quick glance on owl usage, version and special extracts of the GPL license, as required by a GPL3 project. Parameters are explained in the following paragraph. 2.5 Parameters ---------- Any command given to the owl executable after the filename will be executed before program execution. Such commands may be numbers as well as instructions. For instance, after the following call: $ owl aprogram.owl 6 0x89 + O54 aprogram.owl will have decimal 143 (6 + hex 89 = 6 + 137 = 143) and 44 (octal 54) passed to the stack, with 44 on top of stack, before execution. See the factorial script for an example. Notes ----- - the shell governs passing parameters to the program, so if symbols match specific shell patterns, you must escape them; for instance, the code chunk: $ owl aprogram.owl 6 0x89 * won't work, since the asterisk has a special meaning for the shell. Escape it like this: $ owl aprogram.owl 6 0x89 \* The same could be said for the single and double quote, for grave, for dot, for $, for #, for % and other special shell symbols; when in doubt, check your shell help file. With bash, if you include all the parameters into the single quotes (provided you don't use the single quote in your program line), the parameters will be passed as-is to the shell: $ owl 'aprogram.owl 6 0x89 *' This may work for other shells too. In particular, for passing strings, use the shell command: $ owl 'aprogram.owl' \"filename\" - It's not legal to give parameters to the owl executable without a file name; if you need to execute simple code patterns, use the -p option; or else put in mind that parameters are an aid for current program, whose name, I repeat, must be given as parameter! 2.6 owl's environment ----------------- When an owl program is launched, two spaces are created in RAM, in which owl establishes its environment. These are the memory and the variables spaces. The memory space ---------------- The memory space is unique among a program session, even if other programs are launched within the current session as an inclusion or as a module execution; any command or function shares this memory space and may read or write upon it, thus influencing subsequent commands. The memory space is composed by: - an integer stack; storable values are 64-bits integer (if used as 8-bits characters, only the lower 8 bit are used, signed or unsigned depending on contest). The LIMIT constant in dib.h defines the depth of the stack (by default set to 1024). - a 2-cells function buffer, to store two functions, thought as sets of instructions and data; this buffer works as a two-element stack, in effect, but it differs from a stack because any command using one or both functions erases the buffer after finishing its task. The length of each function in the buffer is up to LIMIT characters. - an array of chars, called PAD, holding only signed 8-bits characters, used as a string storage; any command using a string, sets/gets the string from PAD; every string loaded and printed by owl from the source code is temporarily hosted on PAD. The length of the PAD is LIMIT. - an array of integers, called simply array, holding true 64-bits integers; it's organized as a long unidimensional array; it's up to the programmer to write code for using chunks of it, to hold many arrays, or one or more multidimensional arrays, provided memory is enough. The length of the array is set by the ARRAYLIM constant in dib.h (by default set to 32768). The variables space ------------------- The variables space is composed by two parts: the integer variables space and the function variables space; the former can hold integers, the latter may host functions (functions are in effect pure strings which contain executable code). These variables are identified with letters from A to Z for integers and from a to z for functions. These variables are generally unique for the whole owl session, but in case of a module execution, the called module receives a copy of the variables of the caller environment. Thus, any change made to the variables in the called module is not reflected in the caller environment (see section "Advanced commands"). 2.7 Error conditions ---------------- During the execution of an owl program, an unexpected stop may occur; this is due to an error condition. In owl, there are few cases when an error condition can be raised. Code error conditions --------------------- Errors caused into owl code constitute the code error conditions, here summarized: - overflow error; this is rarely met, and its apparition makes certain the fact you messed up stuff into your code - stack empty error; this is common and frequent, and it means you have tried to access an empty stack; generally, a proper revision of the code (through the debugger, maybe), may resolve the error condition Mathematical functions are built in such a way they never return an error condition and always return a valuable number. The timing feature (if required at start) cannot be completed after a code error condition. System error conditions ----------------------- If the owl interpreter cannot access the information you provide for execution, a system error condition is raised: - is not a valid option; this error is shown if you set up an option that owl cannot recognize; c is the incriminated character - no string given to option -p; this error condition appears when you try to use option -p without any string - unable to open file ; I/O error; it generally depends on the underlying OS; you probably spelled out the name wrongly, or gave a wrong path; on some cases it may mean you cannot access the file or the directory where the file is stored, or some severe damage affects your drive (God forbid!) - no input file; you invoked owl all alone A system error condition means no code instruction has been executed. User error conditions --------------------- Generally, a code error condition causes an immediate stop of the execution, so immediate that the (eventual) timing feature is interrupted too. The abort commands (?! and !?) stop the execution as well: if met - in any position - they stop immediately the program, causing a user code condition: the returned code is last value on TOS, or else a failure code (in any case, the abort commands cannot cause a stack empty error). The eventual timing feature invoked at start is not completed. Hope everything is clear. Now, let's go with the language syntax. 3 The language ------------ Let's start bare. 3.1 Comments -------- There are two types of comments: # "sharp" comment (as with shells), which extends from '#' to the end of line (*..*) multiline comment (à la Pascal), on one or more lines, enclosed by (* and *) | Examples -------- # single line comment "some code" # comment beginning in the middle of the line (* this is a multiline comment which extends to many lines *) Notes ----- - comments may begin at any point of the source code. - if you're building a program whose target is speed, use only # comments, since the interpreter stops working when a # in a line is met, if not contained in special commands or operators (see for example the ,# and @# commands); the multiline comment, on the contrary, is parsed char by char, and so is time-consuming. - "sharp" comments may be used to build shell scripts, like in bash, where the first line is #!/path/to/owl; these script may receive parameters in input (as explained in the paragraph "Parameters"); see the factorial script file example (you will understand it in a few pages): #!/usr/local/bin/owl # factorial # calculate factorial number (for 64 bits signed integers, the maximum # value of the input value is 20; larger numbers give useless results) # use: factorial %0>~[?!]?"Factorial of "%." is "%1=[;1.][%1-[%1=~][%2'*$1-]!;.]? After turning it into an executable (chmod +x factorial), an example of usage is: $ ./factorial 7 Factorial of 7 is 5040 3.2 Numbers ------- Numbers are 64-bits integer values (to stay beyond doubt, I here underline owl does not treat floating point numbers), in the range -9223372036854775807 ÷ 9223372036854775807, corresponding to the range -2^63+1 ÷ 2^63-1 or, if you like, roughly -9E18 ÷ 9E18 (versions from 0.7.4 downward used 32-bits numbers in the range -2147483648 ÷ 2147483647 - circa -2E9 ÷ 2E9.) This range is sufficient for any integer use; if you need more space, consider increasing measure units (for example kilometers instead of meters): it may help. 3.2.1 Inputting numbers Numbers may be input (in the source code or by keyboard) in the following ways: - as binary numbers, prefixing a capital B to the numbers, and using only ones and zeros; - as octal numbers, prefixing a capital letter O (oh, not a zero!) to the number, and using digits from zero to seven; - as decimal numbers, entering them simply using the ten zero-nine digits (it's the same dear old way); - as hexadecimal numbers, prefixing 0x or 0X (zero plus x or X) to the number, and using the ten digits and the letters from lower a to f, or from upper A to F (you can mix lowercase and uppercase letters, but the output of hexadecimal numbers will be printed with capital letters: I prefer so). Examples 0x100 # input 256 = hex 100 100 # input 100 = decimal 100 (of course) O100 # input 64 = octal 100 B100 # input 4 = binary 100 0XaA # input 170 = hex aa (AA or aA give the same result) This behavior in input is consistent between code lines (in the source files) and numbers typed from keyboard. 3.2.2 Outputting numbers To set the output view, use the following underscore commands: | command | stack notation | purpose | +-----------+-----------------+---------------------------------------------+ | _b | ( - ) | set binary view | +-----------+-----------------+---------------------------------------------+ | _o | ( - ) | set octal view | +-----------+-----------------+---------------------------------------------+ | _x or _h | ( - ) | set the hexadecimal view | +-----------+-----------------+---------------------------------------------+ | _d | ( - ) | set decimal view (this is the default one) | +-----------+-----------------+---------------------------------------------+ | _& | ( - ) | toggle prefixing an ampersand to hex output | +-----------+-----------------+---------------------------------------------+ (Table 3.2) The underscore command _& toggles the prefixing of an ampersand before printing an hexadecimal number (it does not affect other outputs). Examples _o # sets octal output, so e.g. 100 will be printed as 144 _h # sets hex output, so a 100 will be printed as 64 _&_h # sets prefixing an ampersand to hex output; # now a 100 will be printed as &64 After setting a view, all the numbers will be printed in the desired format until the end of the program or until another view setting. Notes - the input method cannot be changed as the output one; this means there's no way to set the environment to input always, say, octal numbers (e.g. simply typing 64 for decimal 100). The standard input method will always accept numbers in decimal form, unless a letter (with a meaning) is prefixed to the input string. - negative non decimal numbers will be printed using the whole 64 bits; for instance, -32 in decimal will be printed as 1111111111111111111111111111111111111111111111111111111111100000 as binary (64 bits) 1777777777777777777740 as octal (21 triplets + the leftmost sign bit) FFFFFFFFFFFFFFE0 as hexadecimal (16 quartets) This is because the sign bit is the leftmost (and last) bit of the 64 used to represent an integer, and thus all bits count. - considering last previous note, there are some interesting bit patterns (detectable in binary mode) whose analysis is useful: 1111111111111111111111111111111111111111111111111111111111111111 is -1 (64 ones) 1111111111111111111111111111111111111111111111111111111111111110 is -2 (all ones but the first) 1000000000000000000000000000000000000000000000000000000000000000 is 2^63 (all zeroes but the sign bit) That's why the number 2^63 (which should be positive) is printed as -9223372036854775808 and thus negative; in reality it's a positive number that overflowed, setting rightmost sign bit; the C compiler, at this point, knows that the number is not 0 (it would have all bits cleared) and decides that the number is unsigned, picturing it as negative because of the sign bit set. For reference see [1]. This doesn't prevent the system to consider 2^63 a real negative number; in fact, if we try this small sequence: 2 63^2/. we get -4611686018427387904 which is a proper and legal negative number; and notice we have divided by a positive number, thus sign is retained: if it's negative after, it's negative before; in any case, the truth is in the middle: 2^63 is negative because it has the sign bit set, but it's also positive, because the C compiler treats it as an unsigned long long int. I prefer to consider it a negative number because of it's influence on subsequent calculations; in any case, add 1 to 2^63 to get -9223372036854775807, or subtract 1 to get 9223372036854775807: these are the true limits of owl calculations range, which define the field in which calculations are correct. A small observation's worth the trouble, here: - take 0; - add 1; - now keep adding 1s until you get 9223372036854775807, which is the larger positive integer; - add another 1 and you get -9223372036854775808 (the forbidden number); - now add another 1: you get -9223372036854775807, which is a legal negative number (the larger in absolute terms); - now keep adding 1s until you get -1; - another addition of 1 (the last), gets to 0 again. The same approach may be done subtracting 1s, rather than summing 1s. This is what I call the circle of integers, which defines completely the domain of numbers used by owl (except the forbidden one) and it's offered by C signed long long int type and the gcc! The program circle.owl, in the examples for current version, perform this loop, but watch out: DON'T EXECUTE IT! it takes too long to complete! - binary numbers are printed with the bit 0 on the right, so that 13 appears as 1101 (read it as 8 + 4 + 0 + 1 = 13), and not as 1011. - purists will probably state that an octal number should always be written in triplets, and that no less than four (or multiples of four) digits should be used for hexadecimal and binary numbers; owl gently accepts numbers in any way you type it and prints numbers with only the needed digits (except for the negative ones, as seen, printed in full 64 bits). - note that if after any leading characters (B, O or 0x) there's no digit that conforms to the number format, the parsing will stop, and the rest of the input string will be interpreted as usual (in decimal or parsed as commands). Examples B2000 # 2 is not a valid binary number, so the ASCII code # of B (66) and decimal 2000 will be put onto stack B102 # this will put binary 10 (=2) and decimal 2 onto stack O88 # 8 is not a valid octal number, so the ASCII code # of O (79) and decimal 88 will be put onto stack O668 # this will put octal 66 (=54) and decimal 8 onto stack 0xGF # G is not a hex: the zero, and the ASCII codes of # G (71) and F (70) will be put onto stack # (notice that the x is disappeared) 0xFFG # this will put hex FF (=255) and ASCII code of G (71) # onto stack 0xFFG, # this will store hex FF (=255) into var G 0xFFF, # this results in an error; do you imagine why? (of course, hex number FFF (=4095) is put onto stack, and the comma command - see String operators - is called upon this cell, without an argument or, if this argument should be on TOS, it is consumed, and some other command will then run out of arguments, emitting an error.) 3.2.3 Truth values Truth values in owl are simply numbers; true is any other number different than 0, false is 0. In brief: | number | truth value | +---------+-------------+ | 0 | false | +---------+-------------+ | not 0 | true | +---------+-------------+ (Table 3.3) Notes - Truth values are taken into account by the choice (?) and cycles (!) commands (see flow control); they expect a value on TOS to work, a value left by a preceding command or by themselves; this value is consumed and its truth value will set one or another behavior. - The truth values left by test comparison operators are: -1 (all bits set) = true truth value 0 (all bits cleared) = false truth value 3.2.4 Math Operators A small but rather complete set of math/logical/bitwise operators are available; arguments and results are all integer numbers: | command | stack notation | purpose +----------+--------------------+-------------------------------------------+ | + | ( n1 n2 - n1+n2 ) | addition | +----------+--------------------+-------------------------------------------+ | - | ( n1 n2 - n1-n2 ) | subtraction | +----------+--------------------+-------------------------------------------+ | * | ( n1 n2 - n1*n2 ) | multiplication | +----------+--------------------+-------------------------------------------+ | / | ( n1 n2 - n1/n2 ) | integer division | +----------+--------------------+-------------------------------------------+ | \ | ( n - -n ) | negation (2s complement) | +----------+--------------------+-------------------------------------------+ | ^ | ( n1 n2 - n1^n2 ) | nth power | +----------+--------------------+-------------------------------------------+ | : | ( n1 n2 - n3) | nth root, n3 = n2th root of n1 | +----------+--------------------+-------------------------------------------+ | > | ( n1 n2 - b ) | greater than test, true if n1 > n2 | +----------+--------------------+-------------------------------------------+ | = | ( n1 n2 - b ) | equality test, return true if n1 = n2 | +----------+--------------------+-------------------------------------------+ | & | ( n1 n2 - n3 ) | bitwise and | +----------+--------------------+-------------------------------------------+ | | | ( n1 n2 - n3 ) | bitwise or | +----------+--------------------+-------------------------------------------+ | ~ | ( b - ¬b ) | logical not | +----------+--------------------+-------------------------------------------+ | << | ( n1 n2 - n3 ) | shift left, n3 = n1 shifted left n2 bits | +----------+--------------------+-------------------------------------------+ | >> | ( n1 n2 - n3 ) | shift right, n3 = n1 shifted right n2 bits| +----------+--------------------+-------------------------------------------+ (Table 3.4) Notes - the division algorithm has been rewritten; it returns the dividend when the divisor is null; so 4/0 is 4; in this way owl never fails and always produces a result. You may prefer another solution; in this case, build a function that tests for null before division; besides, owl can use two types of integer division; the default is the usual C mode for integers (which simply cuts all decimals, so that 4/3 is 1); the other is the Number Theory mode (the two differ when the dividend is negative); to activate the NT mode, use the -i option at start (you can set this option as default by setting the isNTDiv flag to TRUE in owl.h); you can also (starting from version 0.7.4) toggle its mode in the code with the underscore command _i. - the power algorithm has been rewritten, starting from version 0.7.6 (accordingly with the new long long int type); it considers a minimum of special cases, and deals directly with the integer type for a perfect result in terms of integer numbers; it pays in term of speed: it's 3 to 7 times slower than C powl() function. In any case, you are sure that a correct result is always yielded, in the domain of the integer type. - the root algorithm has been rewritten, starting from version 0.7.6; it always produces a result, and never fails, according to the following principles: * with any base and negative root (like \sqrt[-3]{27}), a 0 is returned * with negative base and even positive root (like sqrt(-27), a 0 is returned (a safe result for impossible roots) * with negative base and odd positive root, a negative calculated value is returned; e.g. 3d root(-27)=-3, because (-3)^3=-27 (this is legal because the function y=x^3 is invertible and monotonic, and this is valid for all odd exponents) * with positive base and positive root (odd or even), a positive calculated value is returned In any case, the calculated nth root of an argument is the greatest integer whose nth power is not larger than argument. - the order of operands for the shifting operators >> and << is the same of the algebraic form; where you'd write a << b in algebraic form, you'll write a b << in owl. Besides, you have also the bitwise operators & and |; obtaining all the other missing bitwise operators is easy. See lecture "Missing bitwise operators". - beware of the order of operands in subtraction and division; they appear in the source as you'd write them on paper in algebraic form: 400 125 - means 400 - 125. The same for division: 400 125 / means 400 / 125. - in owl you have only the tests for equality (=) and greater than (>); are you surprised to know that obtaining all the other numeric tests is as easy as 1-2-3? See lecture "Missing comparison operators". - in owl you have only one logical operator (~); obtaining all the other logical operators is not a hard matter (how? Hint: look at the following examples). See lecture "Missing logical operators". - remember to set the desired rounding mode before calculations; use option -r or command _r to set rounding mode according to your needs; see paragraph "Program options". - some integer functions written in owl are exposed in paragraph "Missing math functions". Examples 4 3-. # calculate and print 4-3 = 1 10 3/. # calculate and print 10/3 = 3.333-> 3 (integer division) 4 0/. # since divisor is null, return 4 2 3^. # calculate and print 2^3 = 8 10 3:. # calculate and print 3rd root of 10 = 2.154 -> 2 27\3:. # calculate cubic root of -27 = -3 27 3\:. # impossible calculation, return 0 4 2&. # calculate and print 4 and 2 = 0 (bitwise) 4 2|. # calculate and print 4 or 2 = 6 (bitwise) 1 1&. # calculate and print 1 and 1 = 1 = true 1 0|. # calculate and print 1 or 0 = 1 = true 4~. # calculate and print not 4 (boolean) = 0 = false 0~. # calculate and print not 0 (boolean) = true = -1 8 2>>. # calculate and print 8 shifted right 2 bits = 2 6 6<<. # calculate and print 8 shifted left 2 bits = 384 3.2.5 Array operators To store/retrieve values to/from the integer array there are some useful operators: | command | stack notation | purpose | +----------+-----------------+--------------------------------------------+ | #, | ( n m - ) | store n at position m of integer array | +----------+-----------------+--------------------------------------------+ | #@ | ( m - n ) | fetch n from position m of integer array | +----------+-----------------+--------------------------------------------+ (Table 3.5) Notes - #, and #@ act upon the integers array. While the PAD can hold only characters (values ranging from -128 to 127), the integer array can hold any kind of integers, in the range of the C int type; each operator must not be put just after a letter, i.e. they must be separated by a blank (or the interpret would see the letter and the comma operator as a storing operation); besides, the couples #, and #@ must be bound, or else they will be seen as sharp comments; in the end, the position of the array element (the index) is always on the left of the operator; look at the stack notation above. - array index starts from 0 and the integer array is 32768 cells wide by default; the underscore command _A return this number on TOS. - pay attention to the array symbol in the #, and #@ operators: in this case # is not a comment delimiter, but rather a symbol for array, where comma (store) and at (fetch) operate as usual. - it's simply impossible to write outside of the array limits, or using negative cell indexes (if you do it, the system will ignore your indications, and convert your cell index to a suitable one). Examples 23456 0#, # store the integer 23456 into position 0 of the integer array 0#@. # retrieve position 0 of the integer array and print it 100 1000000#. # the array cell index is clearly out of bounds, but the # system will turn it into a valid array cell index 3.3 Stack commands -------------- A stack language bases its power on the stack; it's possible to carry out all calculations without variables or other aids; and much of the speed of such languages is justified by this tool; programming the stack is one of the greatest mental challenges I faced during my programmer's life. Stack commands of owl are a small but complete set: | commands | stack notation | purpose | +-----------+--------------------+-----------------------------+ | $ | ( n1 n2 - n2 n1 ) | swap | +-----------+--------------------+-----------------------------+ | % | ( n - n n ) | duplicate | +-----------+--------------------+-----------------------------+ | ; | ( n - ) | drop | +-----------+--------------------+-----------------------------+ | ' | ( x*n m - y*n ) | roll mth element | +-----------+--------------------+-----------------------------+ | ` | ( x*n m - x*n n ) | pick mth element from stack | +-----------+--------------------+-----------------------------+ (Table 3.6) Notes - take note of the "shape" of swap: see it as $wap; and about dup, look at the two circles of %: they symbolize the duplication of TOS. - like forth, indexes for roll and pick start from 0, so to roll 3 elements you use 2', or to pick 3rd element (counting from TOS) you use 2`. - there's nothing like a ROT command. Use 2' instead. - there's nothing like an OVER command. Use 1` instead. - you may notice how many stack commands (with respect to forth) are missing: read lecture "Missing stack commands" to learn how to implement some of them. - small controls have been inserted, starting from version 0.7.6, into the PICK and ROLL commands; if the ROLL or the PICK index is meaningless, is it now ignored. Examples (in capitals the forth equivalent) 23 34$ # SWAP - leave 34 32 23 34 1' # the same (1 ROLL = SWAP) 23% # DUP - leave 23 23 23 0` # leave 23 23 (0 PICK = DUP) 23 45$; # SWAP DROP - leave 45 10 20 30 2' # 2 ROLL - leave 20 30 10 (2 ROLL = ROT) 10 20 1` # 1 PICK - leave 10 20 10 (1 PICK = OVER) 3.4 Storing and fetching commands ----------------------------- The comma and the @ ('at') symbols are very important in owl, and they play a major role; the comma (,) is the general storing operator, while @ is the general fetching operator. The storing and fetching commands must be put immediately after the letter (no blanks or other chars in the middle): | command | stack notation | purpose | +----------+-----------------+------------------------------+ | , | ( [f] v - ) | store [f] into v | +----------+-----------------+------------------------------+ | , | ( n V - ) | store n into variable V | +----------+-----------------+------------------------------+ | @ | ( v - x*n ) | execute function stored in v | +----------+-----------------+------------------------------+ | @ | ( V - n ) | fetch value from variable V | +----------+-----------------+------------------------------+ (Table 3.7) Notes - lower letters a÷z are used for storing functions (which are arrays of characters); upper letters A÷Z are used instead for storing numbers (they are integer variables); the number to be stored for each instantiation is taken from the Top of Stack (and consumed), while the function to be stored is the last function that appeared in the code, and after instantiation the function buffer is emptied (if you happen to store a function which was not in a previous section, no problem: the function stored will be the void function, which does nothing); if you use variables in wrong places (e.g. a÷z for numbers), something weird may happen, such initializing a numeric var with an undesired stack value, or initializing a function var with the void function, but this shouldn't result in a system error (apart from the classic "underflow error"!). - no blank must be put between the variable name and the operator; if a blank is present, the operator will be seen as a string operator (see "String operators"). - remember that, while it seems natural, a direct execution of a function, like [exp]@ has no meaning, because the whole may be substituted by exp itself; different is the case of ? and ! operators, which require one or two proper functions to perform cycles or choices, and cannot work without functions. Examples [1+]a, # store function [1+] into a a@ # execute function [1+] in a (taking its argument from stack) 34000R, # store 34000 into R and drop it from stack R@ # retrieve R value (34000) and leave it on TOS 34000R , # store 34000 in position 82 (ASCII for R) of PAD (see ahead) # because there's a blank between R and , [1+]A, # load a function, then try to store a number into A; # if stack is empty, this raises an error 34000r, # nothing happens (but 34000 remains on TOS and r is # initialized with the last not null function or to []) 3.5 Strings & characters -------------------- 3.5.1 Strings Any text enclosed in double quotes like "text" is directly printed to terminal as string: in practice, every string prints itself. A string is always multiline and no nesting is admitted (but you can escape the double quotes). Any string is also copied to PAD before printing. Nothing is printed after it (not even a New Line, which must be provided by the programmer). Escape char is the backslash \ (as in C). Any text enclosed in double quotes with a double couple at the end like "text"" is put into PAD but not printed. Useful for building patterns. Finally, the double double quotes "" (in practice the empty string, which has no effect in owl) acts as a new line, printing a new line character on the terminal; it's equivalent to "\n" or to 10) and 13) (see ahead). Examples "Hello\n" # print Hello + New Line to standard output "Hello " # do the same (this is a multiline string, # note the closing quotes on a new line) "\tHello\n" # print Hello after a tab, and then New Line "\n" # print a New Line (ASCII 10 for Mac, 13 for Win/UNIX) "" # does the same "Code: xxxx"" # store the string into PAD without printing it (take note of # the closing double double quote); subsequently, chars # at pos 6,7,8,9 may be changed to specific digits, and # the modified string may be printed after each touch with } Notes - because of the property of storing a string without printing it (terminating the string with the two double quotes ""), you cannot write two strings immediately one after the other. While this may sound natural: "Hello ""World!" it's not interpreted in version 0.7.6 like one would believe. The first string "Hello "" is stored but not printed, the chars W, o, r, l, d are put on TOS, a void ! command (a cycle) is executed, and finally a void string is started (and ended, if the file ends just there), so neither of strings is printed. Of course the right way is separate the two strings: "Hello " "World!" The same may be said about the double double quotes (the empty string acting as a new line command); the following is meaningless: """" This may violate the principle of backward compatibility, but I guess this is a minor issue, since two subsequent string are generally not printed this way. - there are no string variables, in owl; nonetheless, you can store strings (to some extents) into functions; for instance: ["string"]s, is a string stored into function s; when executed, through s@, the function will return the string and print it, as expected. - you may wander why I did choose the double double quotes "" for the new line (equivalent to FORTH's CR); well, it was something that's been hanging around my mind for a long time; I noticed in fact that the double double quotes were useless, and since they produced no apparent direct or side effect at all, no user had any reasons for using them. So I decided that the double double quotes, an easy and fast typing, should have merited a place in owl's syntax. 3.5.2 Escape chars into strings owl recognizes C-like escape chars (and even some extended chars, but the latter may not be available to your shell without a specific configuration). The standard ASCII escape chars, available for any shell/editor on any OS, are: | escape sequence | yielded character | +------------------+----------------------------------+ | \0 | (ASCII 00), end-of-string | +------------------+----------------------------------+ | \b | (ASCII 08), backspace | +------------------+----------------------------------+ | \t | (ASCII 09), horizontal tab space | +------------------+----------------------------------+ | \n | (ASCII 10), new line | +------------------+----------------------------------+ | \v | (ASCII 11), vertical tab | +------------------+----------------------------------+ | \f | (ASCII 12), form feed | +------------------+----------------------------------+ | \r | (ASCII 13), carriage return | +------------------+----------------------------------+ | \" | (ASCII 34), double quote | +------------------+----------------------------------+ | \' | (ASCII 39), single quote | +------------------+----------------------------------+ | \? | (ASCII 63), question mark | +------------------+----------------------------------+ | \\ | (ASCII 92), backslash | +------------------+----------------------------------+ (Table 3.8) For what about the extended escape chars, remember that your shell/editor may not be able to correctly display them. In these case you should enable the Latin1 or any equivalent Extended ASCII table (for instance, ISO-8859-15 a.k.a. Latin9, ISO-8859-9 a.k.a. Latin5 and ISO-8859-1 give almost equal results - information grabbed from Bram Moolenaar's gvim encoding tables). Implemented extended escape codes are: | escape sequence | identifier | yielded character | +------------------+---------------------------------------------------------+ | \c | lower c | (ASCII 162) ¢ cent | +------------------+---------------+-----------------------------------------+ | \L | upper l | (ASCII 163) £ pound sterling | +------------------+---------------+-----------------------------------------+ | \S | upper s | (ASCII 167) § paragraph | +------------------+---------------+-----------------------------------------+ | \< | lesser than | (ASCII 171) « left angle quote | +------------------+---------------+-----------------------------------------+ | \m | lower m | (ASCII 172) ¬ logic 'not' symbol | +------------------+---------------+-----------------------------------------+ | \s | lower s | (ASCII 175) ¯ upperscore | +------------------+---------------+-----------------------------------------+ | \o | lower oh | (ASCII 176) ° degrees sign | +------------------+---------------+-----------------------------------------+ | \+ | plus | (ASCII 177), ± plus-minus | +------------------+---------------+-----------------------------------------+ | \2 | two | (ASCII 178) ² superscript two | +------------------+---------------+-----------------------------------------+ | \3 | three | (ASCII 179) ³ superscript three | +------------------+---------------+-----------------------------------------+ | \u | lower u | (ASCII 181) µ Greek mi | +------------------+---------------+-----------------------------------------+ | \x | lower x | (ASCII 183) · middle dot) | +------------------+---------------+-----------------------------------------+ | \> | greater than | (ASCII 187) » right angle quote | +------------------+---------------+-----------------------------------------+ | \A | upper a | (ASCII 198) Æ uppercase Latin AE | +------------------+---------------+-----------------------------------------+ | \p | lower p | (ASCII 215) × times | +------------------+---------------+-----------------------------------------+ | \B | upper b | (ASCII 223) ß s-z ligature | +------------------+---------------+-----------------------------------------+ | \a | lower a | (ASCII 230) æ lowercase Latin ae | +------------------+---------------+-----------------------------------------+ | \- | minus sign | (ASCII 247) ÷ interval or division | +------------------+---------------+-----------------------------------------+ | \O | upper oh | (ASCII 248) ø small Greek phi | +------------------+---------------+-----------------------------------------+ | \T | upper t | (ASCII 254) þ Icelandic thorn | +------------------+---------------+-----------------------------------------+ (Table 3.9) Notes - The \0 (null) escape character may be used into strings, with the effect to stop the string at the first null met; for example: "Anna\0and me!" is a string that will show only Anna when executed, because the \0 sequence interferes with the C-string between the two double quotes. This is the very same reason why the string "\0" acts as "\n": it's seen as "" and, accordingly to the double double quotes behaviour, a new line is printed. - It's a pity that the Latin1 ASCII table doesn't include the Greek pi, but starting from version 0.7.6 you can write a stylized Greek pi with "|\s|" (bar-upperscore-bar), which produces a sort of "mystic" Pi |¯| Drawbacks are that this character occupies three positions and that it's not as beautiful as the real Greek pi. Compilation note The extended Latin1 ASCII Table characters here described are not seen by all shells/editors/Operating Systems. As with time functions this is a feature of owl not completely portable. You'd better test what are the ASCII tables supported by your system, and maybe you can use the same escape codes with different (and maybe prettier) results. 3.5.3 Characters The characters from A to Z and from a to z , if not part of a storing/fetching command and if outside of strings, put their ASCII value on TOS. Some tricks are available at this point: for example, since d is ASCII 100, d2* is 200, while dd* is 10000 and so forth. Notes - if you need the ASCII values of punctuation characters, or extended ASCII table characters, and you cannot find them on your keyboard, you need to directly put the numerical code of the character you want to emit. Examples A # put 65 on TOS z # put 122 on TOS 33 # put ASCII code of ! 248 # put ASCII code of ø (Greek phi) dd* # put 10000 on TOS 3.5.4 String & character operators To operate on strings (specifically onto PAD, which contains the strings) there are some useful commands: | command | stack notation | purpose | +----------+-----------------+-----------------------------------+ | , | ( c m - ) | put char c into position m of PAD | +----------+-----------------+-----------------------------------+ | @ | ( m - c ) | fetch c from position m of PAD | +----------+-----------------+-----------------------------------+ (Table 3.10) Notes - , and @ are the same operators of storing and fetching seen before, used here in a different context. - , and @ act upon the PAD; the same PAD is used by double quotes "", open and closed brackets { and }; it's impossible to write out of the PAD limits, or using negative cell indexes (if you do it, the system will ignore your indications, and convert your number to a suitable index). - each operator must not be put just after a letter, i.e. they must be separated by a blank. - string index starts from 0, which is the first character in the string, and the PAD is 1024 characters wide. - see the adieu.owl example for advanced techniques about string usage, using the string relocator. Examples "hear!\n" # print hear! ("hear!" is also copied into PAD) } # reprint last PAD content, which is of course "hear!" 100 0,} # put 100 into position 0 of PAD ('d') and print "dear!" 0@100=[98 0,}]? # if PAD's 0 pos is 'd', change it with 'b' and print "bear!" 100 1000000, # the PAD's position is clearly out of limits, but the system # will convert it into a suitable cell index Notes - if the character immediately before comma (,) and 'at' (@) is a letter, a space or any other divider must be put between the two; otherwise there's no problem in attaching a number with these commands, because 0@ is not interpreted as 'fetch from 0'. - remember this distinction: A, (no blanks between) stores whatever is on TOS into variable A, while A , (blanks between) stores whatever is on TOS at index 65 (ASCII 65 = A) of PAD (since PAD can store only characters, if the number is beyond range 128÷127, only the lower byte (signed) will be saved). 3.6 Functions --------- A function is a sequence of instructions enclosed by a pair of square brackets; the purpose of a function is to store these instructions for subsequent executions in defined program structures or at delayed times or again in a different programmed order. Functions may expect values on the stack to work, or return values on the stack after execution, or both or neither. There is no distinction between functions or procedures. A function is always multiline and nesting is admitted (and encouraged). In any case, get all square brackets paired! I find functions a very important feature of stack languages who support them (forth and false among these); in fact, one severe limitation of so called structured languages like Java, C, Pascal, is that a function can accept a virtually unlimited number of arguments in input, but return one value only; sometimes, workarounds are possible (like passing parameters by reference, not by values, like in C), but of course this ruins the beauty of the function itself. In stack languages, instead, functions read an unlimited number of parameters from the stack (in forth even addresses of strings, floating point values and particular numbers identifying each word), evaluate them and return an unlimited number of values on the stack, ready for successive words. In owl this is made possible by functions, used in the very same way "false" uses them. Examples [10*] # multiply by 10 its argument when executed [10*+] # two arguments: the first is multiplied by 10, then added # to the second when executed ["Hello\n"] # print Hello + CR to standard output when executed # (This is an easy way to store strings...) [%] # duplicate top of stack when executed Notes - comments into functions should be avoided because: * sharp comments terminate the function as they appear * multiline comments require the parsing of every char, so they slow down the function execution. Anyway, do as you please: provided that functions work, any other thing is obviously legal! - the symbol # may be used to stop interpreting any line; so, if you need to temporarily disable part of the code into a function, put a #; remember anyway that something like: Q@[#]? # intended (wrong!) meaning: if Q, stop won't work, since the # would stop interpreting the inner function only, (the one between [ and ]) and not the line in which we test for Q! The effect is that executions continues after the ? - owl supports the indexed function system: the index is a pointer to a function, to be executed indirectly. This index by default points to function a, which is void (a void function does nothing). See the paragraph about dynamic features for more about its usage. 3.6.1 Functions commands Function are merely strings, that is arrays of characters; so there's a strict relation between strings and functions, and the following commands serve as a link to the two classes: | command | stack notation | purpose | +----------+---------------------+------------------------------------------+ | _, | ( "stream" v - ) | put PAD into var v as function | +----------+---------------------+------------------------------------------+ | _' | ( "stream" v - ) | put PAD into var v as a string | +----------+---------------------+------------------------------------------+ | ,, | ( v - "stream" ) | copy function v into PAD | +----------+---------------------+------------------------------------------+ | _@ | ( "stream" - x*n ) | execute PAD as function | +----------+---------------------+------------------------------------------+ | @, | ( v - ) | store function contained into v to index | +----------+---------------------+------------------------------------------+ | @@ | ( - x*n ) | execute function stored into index | +----------+---------------------+------------------------------------------+ (Table 3.11) Notes - the commands _, (underscore-comma), _' (underscore-tick) and ,, (comma-comma) operate both on PAD and variables; the couple _, and _' are the reverse of the comma-comma command; they enable the storing of PAD (which may be instantiated by a string or by a user input) into a function variable as a sequence of characters (which variable may be executed), or to introduce a string by keyboard, store it into a function variable as a string so that, each time the function is invoked, it prints the string. - the _, and _' and _@ tokens are not to be considered underscore commands, but rather peculiar storing/fetching operators. In particular, they let accept functions in input (remember that any input is stored into PAD), and these functions may be stored or executed. - reversing a function into PAD and going back (commands _, and _' and ,,) let modules pass strings and functions to the caller. See the paragraph about modules. - for what about commands @, (at-comma) and @@ (at-at) see the paragraph about dynamic features. Examples ["string"]j,j,,} # print "string" (quoted) 5"%4+*"j_,j@. # the string "%4+*" is copied into j and executed (yields 45) {a_'a@ # get a string from keyboard and store it into var a # (the resulting function is similar to [" string"] producing # the string when invoked) I also like this mini-program, typed directly on the console: $ owl -p'"owl interactive!\n>"[0@][{_@"\n>"]!' This command starts a mini interactive session, executing repeatedly user's input strings as owl functions; not everything is available (due to the interaction with the shell - in particular the command } does not work), and the first error met (for example, and underflow) stops the session. To exit, type (for example) ?!. It's rather funny. Try it! 3.7 Variables --------- As said in the paragraph about the environment, owl uses two kind of variables: - lower letters from a to z to store functions - upper letters from A to Z to store integers Notes - all function variables are filled with the void function [] at start; you may execute them safely even if void, since a void function is harmless: it does nothing. - all integer variables are set to 0 at start, so they are completely initialized. Throughout the manual there are many examples of usage of variables in all contexts. 3.8 Flow control ------------ owl follows more or less what in forth is the flow control; there are six commands to use in this case, with two symbols only; in the following the description (in parenthesis and in capitals the forth equivalent): | command | stack notation | purpose | +-----------+-----------------------+-----------------------------------------+ | ? | ( b [f] - x*n ) | if b is true execute [f] (IF THEN) | +-----------+-----------------------+-----------------------------------------+ | ? | ( b [f1] [f2] - x*n ) | if b is true execute [f1], if false | | | | execute [f2] (IF ELSE THEN) | +-----------+-----------------------+-----------------------------------------+ | ! | ([f] - x*n ) | repeat executing [f] until it returns | | | | true (BEGIN UNTIL) | +-----------+-----------------------+-----------------------------------------+ | ! | ( [f1] [f2] - x*n ) | while [f1] returns true, execute [f2] | | | | (BEGIN WHILE REPEAT) | +-----------+-----------------------+-----------------------------------------+ | ?! or !? | ( ? - ) | exit immediately (ABORT) | +-----------+-----------------------+-----------------------------------------+ (Table 3.12) Notes - the ? and ! operators need not to be chained to the functions they act upon; you can store functions in a first time and execute the choice or the cycle in a subsequent moment; of course, you must be sure that the proper and right functions are used! - About the ?! or the !? commands: * they behave identically * if stack has at least one value, this the value that owl returns to the operating system after exiting; if stack is void, the code for EXIT_FAILURE is returned. This enables the code returning depending on cases; for normal behaviors, 0?! means 'exit with success'; something like 564\?! means 'exit with error code -564' (the meaning of which is established by the user) * anything following ?! or !?, in the logical program flow, is ignored * remember that, since they cause an immediate stop, the timing option won't be completed (see paragraph about error conditions). Examples A@1=[B@3^B,]? # if A=1 then B=B^3 R@[1+][1-]? # if R is true add 1 to TOS, else subtract 1 10[%.10)1-%~]! # starting from 10, repeat print index and decrement until 0 10[%][%.10)1-]! # starting from 10, while index >0, print it and decrement A@[0!?]? # if A is true (<>0), then bye G@~[?!]? # if G is false (=0), then abort (return whatever TOS value) G@~[4\?!]? # if G is false (=0), then abort (return -4 as error code) 0?! # immediately end program with success Note - See preceding examples: to test for 0> is sufficient the number itself; if it's different than zero, the test is automatically satisfied; so to test a number you will need in the future, don't do something %0> but simply % 3.9 Input/Output ------------ The following commands operate in input and output from keyboard and to screen; they use the stack if they treat numbers, or the PAD if they treat characters or strings. They are: | command | stack notation | purpose | +-----------+--------------------------+-------------------------------------+ | ( | ( - u ) | get an ASCII char from keyboard | +-----------+--------------------------+-------------------------------------+ | ) | ( u - ) | emit u as ASCII char to terminal | +-----------+--------------------------+-------------------------------------+ | < | ( - n ) | read one number from keyboard | +-----------+--------------------------+-------------------------------------+ | . | ( n - ) | print n as number to terminal | +-----------+--------------------------+-------------------------------------+ | { | ( - "stream" ) | read a string from keyboard to PAD | +-----------+--------------------------+-------------------------------------+ | } | ( "stream" - ) | print PAD to terminal | +-----------+--------------------------+-------------------------------------+ | _[ | ( «stream» - x*n ) | execute module,name between _[ and ]| +-----------+--------------------------+-------------------------------------+ | _] | ( «stream» - x*n ) | include file, name between _] and [ | +-----------+--------------------------+-------------------------------------+ (Table 3.13) Notes - the output commands ).} don't print anything after their object. If you want, say, a Carriage Return, add 10) or "\n", or store them as functions into a var, say c, and execute c@. PAD remains unaltered after printing. - each input command (<{ behaves differently: the 'input char' command ( waits for a single char, and after it execution resumes, without echoing the input; the 'input string' command { and the 'input number' command < wait for the Return Key to be pressed, and execution resumes after that (this influences any subsequent printing, which begins on the next line). - if you hit ENTER on input commands without input, the following will happen: - the 'input number' command < will return 0 - the 'input char' command ( will return 10 or 13 - depending on the OS - the 'input string' command { will return a void string. - portability in writing New Lines or Carriage Returns is guaranteed. Try using 10) or 13) on Mac OS X or Linux or (if you can compile on it) on Win32, and you will discover that in any case a New Line is performed. - the input and output commands for numbers support the base system. If you enter a number with < following the rules for hexadecimals, octal or binaries, in their respective syntax, you will enter an hexadecimal, octal or binary number; if you set an output view with the underscore commands _b, _d, _o, _h, _x, the dot command . (which prints numbers to the standard output) will act as expected; in particular, it prints 0 if the number to be printed is negative, for all bases other than decimal; in practice, only decimal numbers may be negative. - the command ( waits for a single char and then resumes (without having to press the Return Key); this feature is available only if the termios.h header file is supported by your Operating System and made available to compilation. This is not the case for Windows. Compiling under Windows makes the command ( act a bit differently: it won't wait for a single char, but rather for the Return Key to be pressed, being the first character of the input string the desired char. The rest of the input is ignored. - the command ) emits as ASCII character the value on TOS, cutting it to an unsigned 8-bits value. If the value is greater than 255, a MOD 256 is performed onto the value on TOS. If the value is negative, a complement to 256 of the unsigned 8-bit reduction is taken. These rules cause, for instance, 65, 321, -191 and -447 return the same character 'A'. In practice, the ) command accepts any integer as argument, but it behaves as if it was an unsigned 8-bit character, so you should do the same. - for what about the include commands, note that the included file is parsed before any other subsequent command; so the stack may be affected, after including any file. For what about the include command and the modules execution command, see section about advanced commands; finally, note that, even if these commands begin with the underscore character, they are not underscore commands. Examples (W, # input a char and store it into W (e.g. 65) W@) # retrieve W value and emit it as char (e.g. A) 10) # emit a New Line/Carriage Return (as "\n") 13) # the same i)d)e)a) # emit the string "idea" as four characters 9) # emit a tab (as "\t") 32) # emit a space (as " ") 65) # emit A (= ASCII 65) 321) # emit A (321 MOD 256 = 65) 191\) # emit A (-191+256=65) 447\) # emit A (-447 MOD 256 = 65) 100. # print 100 _x100. # set output view to hex, and print decimal 100 in hex (64) "[0{_s"\n>"0]!' which executes all the shell commands you type on keyboard; of course, aliases, profiling and the word completion are not available, since the commands are passed "bare" to the underlying shell, and owl string-getter makes no use of input facilities. - the _q command returns the stack quantity, which of course increases stack depth by one; but it refers to the state before its execution; it returns zero on empty stack. It should be used for testing (that is, take some action if stack is empty, and some other if not), like in _q["do this on stack not empty"]? _q~["do this on empty stack"]["...or do that on stack not empty"]? _q["do this on stack not empty"]["...or do that on empty stack"]? - the _e underscore command performs two actions: 1) one is filling PAD with null chars (zeroes), so that it contains a void string (for this sole purpose, "" could be sufficient); this ensures that all cells are emptied, and if you used PAD as an array or you have done some pattern filling, this is a good way to do warm start: 2) the second action is to fill the integer array with zeroes (it is reinitialized). This actions are fulfilled in a fast way, but you could do it manually in owl code, using the _A and _P underscore commands; they return on TOS the upper limit respectively of the integer array and the PAD, and this is useful for array or string treatments. - the _D command activates/deactivates the debug mode; it's a useful option if you want to debug a piece of code and not the entire program. If you start the program in debug mode, the first _D command will disable the debug. As a last remark, remember to clean out all calls to _D after debugging. - commands _i and _r correspond to starting options, and have the same meaning; the difference is that they can operate several times, toggling the current state & behavior. - command _OS is clear per se, isn't it? (Que viva UNIX!) 3.10.1 Correspondence with options Many underscore commands simply respond to the need to more dynamics in using starting options; in effect, if you want to set a rounding mode for your entire execution, using a starting option is more practical, but if you need two or more changing of modes inside your program, you cannot start pieces of it with different options; moreover, if you plan to distribute your sources, the final user is likely not to know the exact sequence of options (even if you write it in capitals on the readme file); for all these reason inserting a command in the source may reveal necessary. That's why I present here a list of correspondences between the two worlds. It is quite pleonastic, since the same letter is used in both contexts, but here it is: - the starting option -i is replicated with the underscore command _i - the starting option -r is replicated with the underscore command _i - the starting option -d is replicated with the underscore command _D 3.10.2 Compilation note The time functions here described, just like the extended ASCII characters visualization (see paragraph about escape chars), are not completely portable, since not all Operating Systems accept the gettimeofday() function (see owl.c). In fact, if you compile under Windows, the time functions (as in my implementation) won't be available. Remember this if you want to make your owl code portable. 3.11 Limits of calculation --------------------- The stack, the strings length, the functions length and the array length are all driven by a bunch of constants defined into owl.h, namely STACKLIMIT, LIMIT and ARRAYLIM; they are sufficiently large to hold all normal behaviours of code; in case you wonder what happens if you go past, remember the following: - the stack returns the overflow error in case you go past its limit, and the program stops - strings and functions are simply cut to the last available character (because of the terminator character available characters are one less then the limit, thus if LIMIT is set to 10, you will have 9 available characters); nonetheless your program will fail, because remaining characters are interpreted as character commands; moreover, in case of strings, terminating double quotes are interpreted, instead, as starting double quotes of another string. An example will clear any doubt: suppose LIMIT is 10 (and thus strings and functions can contain 9 characters): [1234567890%]a, will be seen as [123456789] 0%] a, leaving in 'a' the function [123456789], placing 0 onto stack and doubling it, before storing the function, while "enter a number"<. will be seen as "enter a n" umber "<." printing "enter a n", placing 117 109 98 101 114 on stack (corresponding to ASCII character values u, m, b, e, r) and printing <.: not what you meant, huh? - the array is treated safely, i.e. the module of the index is taken before addressing the value in storing or in fetching; for instance, if ARRAYLIM is 10, and you want to store a number into index 11, the "real" calculated index is 11 mod 10 = 1; the following example will help (with ARRAYLIM set to 10): 24 11, 1@ . will store 24 in index 11, but the value fetched by index 1 is the same. 4 Advanced commands ----------------- 4.1 A Foreword ---------- Writing an interpreter is not always an easy task; in particular there's room for success and failure, and in this chapter you will see some of the former and one of the latter. The failure described here is surely the include command and its history. The include command affair is here explained: for version 0.7.0 I conceived a way to include a file into a source, to avoid writing the same code again and again, in the philosophy "write a function on a file and include it from anywhere". When I was working on version 0.7.4 I designed the modularization of the included files, which could live in their own memory space, as an enhancement of the include command itself, but the fact was that in version 0.7.4 there was no include command. While writing version 0.7.6, I realized I had made two mistakes: the first was the fact I had two versions with two different approaches to the include command, the second was that the current syntax for inclusion (versions 0.7.0 and 0.7.2) and modularization (version 0.7.4) couldn't be used into functions, because they caused the interpreter to misinterpret the code: in fact there was no possibility to write: [ ]filename[ ]? being the first open [ bracket and the last closed ] bracket the function limits, and the token ]filename[ the include command; the interpreter would have seen the first pair [] as a void function, the 'filename' a sequence of chars and the last pair [] as another void function. So I decided I had to redesign the whole matter. In the following you will also read about some successes: the advanced features of owl, written for enhancing its behavior: the dynamic features, the module system and the file inclusion (now both present and cohabitant). Read on. 4.2 Dynamic features ---------------- owl supports dynamic binding of functions, a very advanced feature. It consist of an index pointing to a function; executing the index means executing the function pointed by index. The index is symbolized by the @ symbol, which must not be confused with the fetch command and may freely set to point to different functions. A main use of this feature is the ability to write a function, say a, that executes some code, and containing the execution of the index; before executing function 'a', you may set the index to point to, say, 'f' or 'g', depending on some initial conditions; so, when function 'a' is executed, it will execute 'f' or 'g', without the need to rewrite 'a' to adapt it to changed conditions. 4.2.1 The syntax Here are the commands specific to the dynamic indexing functions features: | command | stack notation | purpose | +----------+-----------------+------------------------------------------+ | @, | ( v - ) | store function contained into v to index | +----------+-----------------+------------------------------------------+ | @@ | ( - x*n ) | execute function stored into index | +----------+-----------------+------------------------------------------+ (Table 4.1) 4.2.2 Example Here's an example of indexing programming: #********************** # indexing.owl # a program to show how to use indexing functions # this is the base function # Notice the @@ (at-at) token, which executes indexed function ["This is the name: "@@".\n"]m, # Actually, if executed now, the @@ token would execute function a, # which is void # Now we define three analogue functions ["Ellis Miles"]a, ["Alice Irons"]b, ["Lance Stone"]c, # main # now we cycle through functions, using indexed name variables # Notice the @, (at comma) token, which sets index to point to function a@,m@ # store 'a' into index and execute m (which executes index) b@,m@ # store 'b' into index and execute m (which executes index) c@,m@ # store 'c' into index and execute m (which executes index) #********************** The output is: This is the name: Ellis Miles. This is the name: Alice Irons. This is the name: Lance Stone. 4.2.3 Conclusion Now programs that use functions not known at the beginning of execution may be built, or insert function and data "on the fly" is made possible. Great flexibility may be achieved by your programs, this way. 4.3 The modules system ------------------ An owl project may be composed of more than one file source. Any module that is part of a project may be called (in specific program points) through the _[...] directive (remember that the file name must not be enclosed in double quotes, must not include neither the ']' character nor the '[' character and may omit the extension if it is .owl), like in the following examples: _[math.owl] # module math.owl is called _[math] # module math is searched, and if not found math.owl is loaded A module has a separate variables space, giving much more power to owl itself. There's no limit in the calling depth, but the process memory limit given by the underlying Operating System. Due to this, any variable in a module has a completely local scope: no change is passed back to the caller; so any change can be safely made to module variables, whose effect lasts only until the module is in execution. Knowing this, we must now learn two things: how to pass data to modules (numbers, strings and functions) and how modules return data to the caller. 4.3.1 Passing data to modules When a module is under execution, it inherits a copy of all the variables of the caller. So, passing a variable to a module is easy, because there's no need to implement a specific procedure, it's enough to instantiate it before calling a module; then, as seen before, any variable may be used, ignored, or re-instantiated by the module itself, and all this doesn't affect the caller environment at all, and strings in PAD will remain available for the called program. For example, the line: "here"24A,_[calc.owl] will cause the string "here" to be put onto PAD, and variable A to be instantiated to 24; the module calc.owl will have variable A (its own copy) set to 24 and may use the string in PAD. The same can be said for functions: [1+]a,_[calc.owl] When we want to pass numbers we could also use the stack, because it's really the same of the caller, and there's only one stack for each owl process (independently of the number of modules it calls). When we want to pass strings or functions (which are, someway, strings too), we could also use, besides the PAD, the _, (underscore-comma) or the ,, (comma-comma) commands. 4.3.2 Passing data from modules Passing data back from modules is a different question; since the module variables are a copy of the original caller variables, instantiating a variable has no effect, since the caller will not see this change. To resolve this, let's divide the problem. * Numbers When we want to pass back a number (which is an integer), the only and safe way is to use the stack since, for each process, there's only one stack. Of course, this method permits passing back any number of return values, depending only on stack maximum dimension. * Strings & Functions When we want to pass strings or functions, we must use the PAD, and possibly use a string relocator (for multiple strings). In this case, a string may be copied directly into PAD (eventually ending it with a double double quote, to prevent owl to print it); a function may be reversed into PAD with the ,, (comma comma) command. The PAD, then, will be passed back to the caller "as it is" and reversed in a function at need. 4.3.3 Example Let's see an example; we have a main file: #********************** # Caller (main program) # global function that will be redefined into modules ["Hello, I'm the caller!\n"]a, # global variables that will be inherited by modules 24G, a@ "My G value is originally "G@.10) _[module1.owl] a@ "My G value is "G@.". Is it right?\n" #********************** The module1.owl file is the following: #********************** # Module 1 - first level # redefinition of the function [" Hello, I'm module 1\n"]a, a@ _[module2.owl] a@" After module #2, my G number is "G@.10) " Now, I'm going to reset it to 1024: "1024G,G@.10) #********************** The module2.owl file is: #********************** # Module 2 - second level # redefinition of the function [" Hello, I'm module 2\n"]a, a@ " and this is the original G value I inherited: "G@.13) # redefinition of the value " Now, I'm gonna reset it to 64: "64G,G@.10) " What will module #1 print? And the caller?\n" #********************** The output is: Hello, I'm the caller! My G value is originally 24 Hello, I'm module 1 Hello, I'm module 2 and this is the original G value I inherited: 24 Now, I'm gonna reset it to 64: 64 What will module #1 print? And the caller? Hello, I'm module 1 After module #2, my G number is 24 Now, I'm going to reset it to 1024: 1024 Hello, I'm the caller! My G value is 24. Is it right? 4.3.4 Conclusion This is in all a powerful feature, which could be used for building even large projects, going beyond the 26+26 variables. 4.4 Including a file ---------------- If you need to consult a file (or many files) before executing your source file, you cannot include it as module, because all the variables a module will set (mainly functions) won't be passed back to the caller; in effect, only the stack will be affected. In this case you should use the include command, which was available since version 0.7.0 with a different syntax, but not in the outcast version 0.7.4 (remember that the file name must not be enclosed in double quotes, must not include neither the ']' character nor the '[' character and may omit the extension if it is .owl), as in the following examples: _]geo.owl[ # file geo.owl is loaded _]geo[ # file geo is searched, and if not found file geo.owl is loaded (Note that, with respect to the module inclusion, square brackets are reversed.) As you may expect, the included file is simply parsed as its content was in the very same place of the include command, and all the variables and the stack of the caller will be affected; this may be very useful in case of libraries: a library may be used by more programs, avoiding inserting the same code into each of them. 4.4.1 Example Let's suppose we write some specific functions for dealing with strings (here the string relocator and the string length counter as an example); first of all we write the library file, or we grab it from a directory: #********************** # ( p1 p2 - ) r, string relocator # copy the string from position p1 to position p2 (terminating null included) # it may overlap, so don't copy strings to positions inside the string itself [$[%@][%@2`,$1+$1+]!;0$,]r, # ( p - n ) l, string length # return length n of string starting at p (counts until the null terminator) [0$[%@0>][$1+$1+]!;]l, #********************** and we save it for later use in the file library.owl. Now we have to write a program and we need both functions; we need not reinventing the wheel, nor we have to type the whole commands or to copy & paste the text of library.owl; we simply instruct main program mainlib.owl to include it: #********************** # program to do this and that ["Hello\n"]w, # welcome _]library.owl[ # include library "Type in a string: "{0l@39)}39) " has "." letters!\n" #********************** Now executing: $ owl mainlib.owl a possible output may be: Type in a string: La Marianna la va in campagna 'La Marianna la va in campagna' has 29 letters! The main program is smaller, more elegant and above all it can do everything we need using something we have not to test, because we know it works; and not everything may be used by the caller (see the string relocator function), and thus, if needed, all unused function variables may be safely reinstantiated. Finally, remember that, in case of inclusion of more that one file, if two included files fileA and fileB instantiate the same function variable, the last stored function is the one the caller will see. Avoid overwriting functions, or at least, know what you're doing! 4.5 Recursion --------- One of the most useful features of a programming language is the recursion. The ability to call itself makes a function useful in certain cases where there's a finite and predictable path in computing; a fair example is the factorial calculation, defined for integer N as the product of all numbers from N down to 2 (of course even down to 1, if you prefer, since 1 is neutral to multiplication). A simple algorithm is: Function f(integer N): if N > 2 then multiply N by f(N-1) else multiply N by 2 Multiplication proceeds from the lower terms to higher, because recursion postpones all calculations (in effect the first calculation is 2*3=6, followed by 3*6=24 and so on until N*(N-1) which was in effect the first to be elaborated). Postponement makes recursion not suitable for long or very deep calls, because any computer either has a finite number of available inner recursion calls, or it has a finite stack or it has a finite memory. Or all. In owl, this algorithm is easy written, since recursion is applied simply calling the function inside the body of the function itself; in fact, during the storing of the function, no check is done about syntax (it is delayed to the execution phase). So when the code is parsed, the function is simply called again (and it succeeds because it had already been stored); as an example: [%2>[%1-f@*]?]f, Remember though that using recursion is a risky task, so be certain that your algorithm converges. See preceding example: the number is checked if greater than 2 (and so also positive) and decremented in the body of the ? clause; at a certain point, the two conditions will meet, causing the algorithm to stop. Besides, the multiplication has been set into the ? clause, so that a negative number (or 0 or 1) are simply passed back to the system. This algorithm converges for all integers whose factorial is into owl's integer domain. 4.5.1 Practical problems in using recursion If I call the factorial function this way: 888f@ my computer (an emac G4) stops with "segmentation error" while elaborating 743 (it means I can do 888-743=145 steps in the recursion, before the computer calls a halt). If I call the function this way: 80f@ (note that I ask for less than 145 inner calls, this time) the computer answers 0. Why? At a certain point, it reaches a number so great that the computer overflows. All calculations are now out of control, until a fatal combination: a 0 result, which affects all remaining calculations. 5 Debugging owl programs ---------------------- Running owl with the -d option activates the debug feature; for each command under execution a line is printed containing a complete representation of the stack, with the bottom on the left and the top on the right, and containing the instruction which is about to be performed printed after a double dash. This feature is wonderful, to understanding what your program does and why it does not do what you have instructed it to do. The flow of the program becomes clear, the stack gets populated and you can easily understand where the trouble begins. Moreover, if your program or function alters the stack in some unpredictable way, or fills the stack with unwanted numbers (sort of rubbish), you can see it directly without having to write specific control code. Let's see an example of a debug output for this program called square.owl; first, create the program with the following content: #********************** # this function calculates the square of the number on TOS (* if it's greater than zero, otherwise leaves -1 *) [%0>[%*][;1\]?]f, 3\f@.10) _q _& _[included.owl] "String" "String not to print"" B111A,A@. O222. #********************** Second, create the following fake inclusion program: #********************** # this file is a fake library to be included ["I'm function g\n"]g, ["I'm function w and I print a 24:"24.]w, #********************** Third, run the program with $ owl -d square.owl This is the output: [ ] -- [ (loading function) [ ] -- f, [ ] -- 3 [ 3 ] -- \ [ -3 ] -- f@ [ -3 ] -- % [ -3 -3 ] -- 0 [ -3 -3 0 ] -- > [ -3 0 ] -- [ (loading function) [ -3 0 ] -- [ (loading function) [ -3 0 ] -- ? -- [;1\] [ -3 ] -- ; [ ] -- 1 [ 1 ] -- \ [ -1 ] -- . -1 [ ] -- 10 [ 10 ] -- ) [ ] -- _q [ 0 ] -- _& (prepending & to hex numbers enabled) [ 0 ] -- _[included.owl] (module) [ 0 ] -- [ (loading function) [ 0 ] -- g, [ 0 ] -- [ (loading function) [ 0 ] -- w, [ 0 ] -- "String" String [ 0 ] -- "String not to print"" [ 0 ] -- B111 [ 0 7 ] -- A, [ 0 ] -- A@ [ 0 7 ] -- . 7 [ 0 ] -- O222 [ 0 146 ] -- . 146 Let's look at it; the stack appears always in its entire depth between [ and ], three characters to the right of the margin, all numbers are signed and in decimal format; now, what follows the double dashes shows what is the instruction about to be performed, along with some complementary information; in detail: - in the first two lines a function is being analyzed and stored; - the 3 in the third line means that number 3 is about to be stored; notice that numbers input as binary, octal or hexadecimal appear with their prefixed identifier (B, O or 0x) - the \ is the negate command that will be performed on TOS; you can see the number -3 in the next stack picture - in the next line, the f@ means a function stored into f is about to be fetched (executed) - % 0 and > are the function instructions that follow (the second being the number zero) - the following three lines show that an if-then-else has been analyzed and stored; in this case, two options are available: if the value on TOS is true or false, the one or the other function will be chosen; the debugger prints after the ? what is about to be executed; this works also for the if-then (except than, in case the value on TOS is false, nothing is printing after the ?), the repeat-until and the begin-while-repeat - the ; 1 and \ are the commands executed according to the ? choice - the dot is the instruction that follows, causing the output -1 to appear - the 10 will be stored; notice that this debug line appears on a new line Note for C programmers: actually, the fact the debug line appears on a new line happens if the ftell(stdout) C function returns other than -1 (which means EOF error); in Mac OS X this works, in Linux it seems not. In all cases where this doesn't happen, the line appears three characters after last printed character. - the ) emit the character 10 (carriage return) to the standard output; notice the void line - the _q and _& are two underscore commands (notice the comment which clarifies the operation performed with _&; the same for commands _i, _r and in general for all toggling commands) - the _[incl.owl] is a file inclusion (a module, as reported; in case of an included file, the word 'inclusion' is reported) - in the next four lines, the analysis and storing of functions g and w is performed - in the next line, a string is loaded and printed; if the string continues on next line, an ellipsis is shown after what's contained into the first line, to signal that the string is not finished - in the next line, a string is loaded but not printed (it terminates with the double double quote) - then, the binary number B111 is put onto stack - in the next line, the value on TOS is stored in the variable A - in the next two lines, the value in A is fetched and printed - finally, the binary number O222 is put on TOS and printed All comments are discarded. Here's now a handy table for owl's debug entities: | token | notes | +-----------------------+-----------------------------------------------------+ | [ (loading function) | the loaded function is not printed | +-----------------------+-----------------------------------------------------+ | f, | a function is stored (f=a..z) | +-----------------------+-----------------------------------------------------+ | f@ | a function is executed (f=a..z) | +-----------------------+-----------------------------------------------------+ | N, | a number is stored (N=A..Z) | +-----------------------+-----------------------------------------------------+ | N@ | a number is fetched and put on TOS (N=A..Z) | +-----------------------+-----------------------------------------------------+ | _c | underscore command (the proper identifier appears); | | | if the command toggles a feature, the new behavior | | | is reported | +-----------------------+-----------------------------------------------------+ | _[...] (module) | module execution | +-----------------------+-----------------------------------------------------+ | _]...[ (inclusion) | file inclusion | +-----------------------+-----------------------------------------------------+ | "string" | string printed (and stored into PAD) | +-----------------------+-----------------------------------------------------+ | "string"" | string stored into PAD and not printed | +-----------------------+-----------------------------------------------------+ | | a number (eventually prefixed by B, O, 0x) is put | | | on TOS | +-----------------------+-----------------------------------------------------+ | ? [...] | if-then(-else); the chosen function is printed | +-----------------------+-----------------------------------------------------+ | ? | if-then without chosen function (false case) | +-----------------------+-----------------------------------------------------+ | ! [...] | repeat-until cycle | +-----------------------+-----------------------------------------------------+ | ! [...][...] | begin-while-repeat cycle | +-----------------------+-----------------------------------------------------+ | | any other character(s) is a specific function or | | | command | +-----------------------+-----------------------------------------------------+ (Table 5.1) Notes - You can activate the debug mode "on the run", by means of the underscore command _D; each _D toggles the debug state, so you can enclose a piece of code between two _D, and only that piece of code will be analyzed by the debugger. If you start owl with the -d option, the first _D will shut down the debugger, rather that enabling it. - The cycles executed by ! in debug mode are quite dangerous: they are evaluated many times, and all the times the debug lines are printed. So the output is long. A trick to solve this problem is to use the _D underscore command before and after the function (including the ! command), to exclude the function from being debugged; this is good only if you proved your function to be correct. - Using the debugger, the natural output of the program is destroyed, since debug lines interfere with it, but debug lines and program output should not mix, anyway. - The debug function is available even with the -p option. - Notice that all numbers in the debug lines will be printed in decimal, regardless of the particular output view. - The -D and -S options work only if the -d option has been set. The first (-D) prints all variables content on the same debug line; the second (-S) enables the single step execution: at each key-press, the program will execute a command; if you press S or s, though, the single step execution will end, and the normal debug will operate until the end of the program. The -S option is not available under Windows. If you learn using this debugger, you'll be able to inspect the program flow, reading the stack and understanding why your program is not working. If you want to do some analysis in later times, you can store the output in a file as in: $ owl -d square.owl > square.txt The file square.txt is a simple text file that can be printed, read, modified, copied, emailed and so forth, for easy and manageable postponed analysis. 6 Lectures -------- 6.1 A foreword ---------- During the development of owl, I wrote something for testing the program, and all my attempts gave birth to a collection of documents. These documents are not math articles, just some speech about the matters I faced in writing the interpreter. All the "missing" chunks of code found are exposed [into square brackets], to ease the comprehension of the fact they must be used as functions. 6.2 Missing comparison operators ---------------------------- The only comparison operators present in owl are the = (is equal) and > (is greater) operators, but it's not difficult obtaining all others, making use of the logical operator ~, which inverts the truth value of the number on TOS; let's review them: - not equal Trivial: [=~] - lesser than Well, "n1 is lesser than n2" means "n2 is greater than n1", so lesser than is [$>] - lesser than or equal Now, "n1 is lesser than or equal to n2" means "n1 is not greater than n2", so lesser than or equal is: [>~] - greater than or equal Of course "n1 is greater than or equal to n2" means "n1 is not lesser than n2" so, basing on the previous definition of 'lesser than', we get: [$>~] 6.3 Missing stack commands ---------------------- forth is the prototype language for stack-based projects: owl doesn't refuse the confrontation with his grandfather but, since it's is a tiny language, it hasn't all the features and commands of it; nonetheless, at least those managing the stack are worthwhile to be examined. The commands of classic forth, already present in owl, are listed in the following table: | forth | owl | +--------+-----+ | DEPTH | _q | +--------+-----+ | DROP | ; | +--------+-----+ | PICK | % | +--------+-----+ | ROLL | ` | +--------+-----+ | SWAP | $ | +--------+-----+ (Table 6.3) In the following there are some addresses for simulating missing forth commands, for what's in owl power (this section is partly derived from the bogusforth manual); the description sometimes refers to slowness of some, if they depend on ` (pick) or on ' (roll), because complex iterations are involved when these commands are invoked, although for 1 or 2 elements such slowness is negligible. Let's go. - 2DROP ( a1 a2 - ) This operation drops the two topmost items. Very easy: [;;] - 2DUP ( a1 a2 - a1 a2 a1 a2 ) This operation duplicates the two topmost stack items in order. Not so easy; there are two ways: 1) The first is the easier, but also not so fast: [1`1`] 2) The second is smartest, but longer to digit: [$%2'%2'$] - 2OVER ( a1 a2 a3 a4 - a1 a2 a3 a4 a1 a2 ) This operation duplicates the two items under the two topmost items. The only way to do it in owl is: [3`3`] - 2ROT ( a1 a2 a3 a4 a5 a6 - a3 a4 a5 a6 a1 a2 ) This operation rotates the two items under the four topmost items. The only way to do it in owl is: [5'5'] - 2SWAP ( a1 a2 a3 a4 - a3 a4 a1 a2 ) This operation swaps the two items under the two topmost items. There are two ways: 1) The first is the easier, but also not so fast: [3'3'] 2) The second is smartest, but if you study the formula, its only a rewriting of the first: [2'3'$] - ?DUP ( a - 0 | a a ) This operation duplicates TOS only if it's not null: [%[%]?] The ? chooses to % (duplicate) if TOS is True (i.e. not 0), or else does nothing, leaving TOS (which is 0) untouched. - NIP ( a1 a2 - a2 ) This operation drops the element under TOS. Easy: [$;] - OVER ( a1 a2 - a1 a2 a1 ) This operation copies on TOS the element under the topmost item. There are two ways: 1) The first is the easier, but also not so fast: [1`] 2) The second is longer to digit: [$%2'$] Notice the similitudes with 2OVER. - ROT ( a1 a2 a3 - a2 a3 a1 ) This operation rolls third element under TOS to TOS: [2'] - -ROT ( a1 a2 a3 - a3 a1 a2 ) This operation rolls TOS and put it as third element under TOS, and is equivalent to two ROTs: [2'2'] - TUCK ( a1 a2 - a2 a1 a2 ) This operation places a copy of TOS under the element under TOS. Easy again: [%2'$] 6.4 Missing math functions ---------------------- Here's a quick and dirty list of substitutes for some useful math integer functions: - ABS We must check if the number is negative: if it is, we negate it: [%0$>[\]?] - MOD (remainder) ( n1 n2 - n3 ) Given n1 / n2, MOD returns the remainder (or modulus): n3 = n1 - int(n1 / n2) * n2 The function here presented returns divisor if dividend is null: [%[1`1`/*-][;]?] - /MOD (quotient and remainder) ( n1 n2 - n3 n4 ) Given n1 / n2, /MOD returns the integer result and the remainder: n4 = int(n1 / n2) = quotient n3 = n1 - n4 * n2 = remainder The function here reported returns divisor if dividend is null: [%[1`1`/2'2'2`*-$][;]?] 6.5 Missing logical operators ------------------------- Bitwise operators work on an integer bits, matching them one by one; for this reason they cannot be used always as logical operators. Bitwise operators present into owl commands are & (and) and | (or), while only one logical operator is present: the ~ (not) operator which reverse the logical meaning of a truth value. I recall here that pure logical values are those that when false have all bits cleared (0), and when true have all bits set (-1). The ~ (not) operator has this capability: when it negates a false value (all bits cleared), it returns a value with all bits set (-1); when it negates a true value (some bits set), it returns a value with all bits cleared anyway (0). Here's the truth table for logical operators we are trying to derive from combinations of owl commands: | A | B | AND | OR | XOR | IMP | EQV/XNOR | NAND | NOR | +-----+----+------+-----+------+------+-----------+-------+-----+ | T | T | T | T | F | T | T | F | F | +-----+----+------+-----+------+------+-----------+-------+-----+ | T | F | F | T | T | F | F | T | F | +-----+----+------+-----+------+------+-----------+-------+-----+ | F | T | F | T | T | T | F | T | F | +-----+----+------+-----+------+------+-----------+-------+-----+ | F | F | F | F | F | T | T | T | T | +-----+----+------+-----+------+------+-----------+-------+-----+ (Table 6.4) The stabilizing function Now, to implement these functions and make them working correctly, either we're sure to have all truth values as arguments (-1 and 0) or we must use an intermediate function to transform any "true" value (which is any integer different from 0) to the true "truth" value -1; we could use the following stabilizing function: [~~$~~] since two "not" operators change any value into a truth value; in fact, if we have 4 and 2 to test with & (bitwise and), we should obtain "true"; but, since & is a bitwise operator, it returns 0 (and thus false), because no bits of 4 and 2 match. Using twice the "not" operator on a value is the right thing to do: for instance, 4 (true) after the first "not" becomes 0, and after the second "not", becomes -1 (again true); 0 (false) after the first "not" becomes -1 and after the second "not" is 0 again (and thus false again). Now that we know how to stabilize truth values, we can implement the missing logical operators as functions: - logical AND From table 6.4 we know how the logical AND function should work; we can use the bitwise version of the command, using the stabilizing function upon the arguments: [~~$~~&] - logical OR The same reasoning can be applied here for the OR logical function: [~~$~~|] - logical XOR From Table 6.4 we see that XOR = B if A is false, and XOR = not B if A is true; so the following rule applies: [$[~]?] which is simple, beautiful and gives the right results. Above all, it may be used with any value of truth (0 for false, not 0 for true), avoiding thus & and | which are, in owl, bitwise operators (see ahead). If we want it to return always truth values (0 or -1), we could negate twice the result: [$[~]?~~] - logical IMP Implication is false only if the first truth value is true and the second is false, as in the Table 6.4: negating the second argument and performing an AND gives true only if the first argument is true and the second is false, which is right the opposite of the implication; thus, negating this result yields the following wonderful implication rule: [~&~] Another version, equally beautiful and simple, is: [$~|] In the preceding formulas, the bitwise & and | may be used safely, since ~ is logical. The first is anyway preferable, since it always returns truth values (0 or -1). - logical EQV/XNOR The equivalence is the negation of the XOR logic rule (and thus it can be named XNOR also). The formula thus come in the same two flavors: [$[~]?~] [$~[~]?] The first is preferable, since it always returns truth values (0 or -1). - logical NAND This function is equal to "not and"; in BASIC it would express as NOT(X AND Y), but owl, being stack based, can simply negate the AND function, returning thus only truth values: [~~$~~&~] - logical NOR Again, this function, being "not or", is expressed negating the OR function: [~~$~~|~] 6.6 Missing bitwise operators ------------------------- For what about the missing bitwise operators, we start from what we have: two built-in bitwise operators, & (and) and | (or); the ~ (not) operator is instead logical, and the XOR operator (bitwise or logical) is totally absent. How to get them? Let's see in detail (we will use here a 32-bits pattern, for simplicity, but all can be easily transposed to a 64-bits model): - bitwise negation: 1s complement 1s complement consists in the total inversion of the bits of a number, taking into account all the bits of the number representation (say 64 for a 64 bits integer and so on). As an example, consider 45, which in binary is 101101 (2^0 is on the right); of course, to follow the complete 32-bits binary representation, we should write: 0000000000000000000000000101101 with all the remaining bits null. Inverting all the bits leads to the following: 1111111111111111111111111010010 the final 1 on the left sets the negative number; this is the binary form of -46 in 32 bits, which is the 1s complement of 45. Nothing changes for a 64 bits representation, as in owl capabilities. The 1s complement of -46 is (of course) 45, since it's a complete reciprocal bits reversing, and so the double negation returns the original argument (as it should be). The algorithm for the bitwise 1s complement in owl is the following: [1+1\*] This function, yes, yields the bitwise NOT result through a workaround, but the final resulting bit pattern is the right one (and independently of the machine core architecture). - bitwise negation: 2s complement The 2s complement is the bit pattern definition of the simple negation; the mathematical definition is: negate all bits and add 1 (that is, operate a 1s complement and add one). Let's consider again the number 45: the 1s complement, as we saw, is -46; if we add 1 we get -46 + 1 = -45 which is the negation of the number 45. Let's now operate upon -46; its 1s complement is 45; adding one we get 46, which is the negation of the original argument -46. owl, through the operator \ (negate) treats correctly (as long as C treats correctly) the negation of any number, while the purest 2s complement procedure may give unpredictable results with certain bits patterns (see [2]). Summing up, to set up a function that implements the 2s complement, we could use (trivially): [\] or we could make use of the standard math definition of the 2s complement (with care): [1+1\*1+] - bitwise XOR The definition for the 'xor' operator is XOR(A,B)=NOT(A AND B) AND (A OR B). We must ensure that this definition - which applies to truth values and so is targeted for logical functions - may be adapted for bitwise purposes. We already have & and | as bitwise operators, and we also have the 1s complement function, so a bitwise implementation could be: [1`1`&1+1\*2'2'|&] Let's check it. Let's take 7: 0000000000000000000000000000111 and 56: 0000000000000000000000000111000 Now XOR-ing bit by bit means setting 1 if correspondent bits differ, and 0 if they match; this is the result we should obtain: 0000000000000000000000000111111 This corresponds to 63, which is precisely yielded by our bitwise XOR. - bitwise IMP The bitwise IMP applies bit by bit the principle that it returns false only if the premise (the first argument bit) is true and the consequence (the second argument bit) is false. We use the same formula for the logical IMP, substituting the 1s complement to the ~ logical operator: [1+1\*&1+1\*] Note that this operator is not symmetrical, and thus the order of operands is important and not commutable. Let's put it at work (considering that, in this case, the order of the operands has its importance); we have 56 (in 32 bits): 00000000000000000000000000111000 and 15: 00000000000000000000000000001111 Applying the IMP rule from 56 to 15 we have: 11111111111111111111111111001111 which is the representation of -49, the result yielded by our function. And if we invert the operands, applying the IMP rule from 15 to 56, we have: 11111111111111111111111111111000 which is the representation of -8, the very same result returned by our IMP function. - bitwise EQV/XNOR As seen in the logical operators examples, the EQV operator is the opposite of XOR (and thus called XNOR also). We already have the bitwise XOR operator, so we could negate this operator's results, using of course the 1s complement: [1`1`&1+1\*2'2'|&1+1\*] Or better, we could consider that if XOR(A,B)=NOT(A AND B) AND (A OR B) then NOT XOR(A,B)=(A AND B) OR NOT (A OR B), and thus: [1`1`&2'2'|1+1\*] which is shorter. Let's check it with 7 and 56 as before, so the result bit pattern should be: 11111111111111111111111111000000 which is the binary form of -64 in 32 bits, and this is what we get with our EQV function. - bitwise NAND Negating (in the sense of the 1s complement) the bitwise & we already have, leads to the bitwise NAND: [&1+1\*] As an example, consider 3: 00000000000000000000000000000011 and 30: 00000000000000000000000000011110 Now we must put a 0 where the bitwise & gives true (only when set bits match) and 1 everywhere else. So the result should be: 11111111111111111111111111111101 This is -3 in 32 bits, and it's precisely what our NAND function gives. - bitwise NOR Negating (again in the sense of the 1s complement) the bitwise | we dispose of, brings the bitwise NOR: [|1+1\*] We test it with the same numbers as before, 3 and 30, so we should see a 1 where | fails (only when zero bits match) and 0 everywhere else. The bit pattern result is: 11111111111111111111111111100000 This is -32 in 32 bits, the same result our bitwise NOR brings. 6.7 The begin-until and the begin-while-repeat cycles ------------------------------------------------- Begin-until and begin-while-repeat are two of the most powerful structures of forth; they perform loops whose length is not known at the start, because a specific condition must happen for the cycle itself to stop (say, a value becomes negative, two variables get equal, a value surpass a limit, and so on). owl has such two structures built-in. They are both driven by the ! command, which determines which of the two must to use by the number of functions in the function buffer (which, I recall here, is a sort of a two elements stack holding functions): if there's one function only it's a begin-until (the condition is built into the function, in the sense that the function itself must leave a value on TOS that is evaluated, and if false the cycle is repeated), like in: 10[%.1-%0>~]! 10 is put on TOS; then the function prints a copy of TOS, decrement it and compares it to 0, leaving a flag of the negation; read it as if: Print a copy of TOS and decrement TOS until it's not greater than zero. If there are two functions, it's a begin-while-repeat (the condition is evaluated straight away executing the first function and if a true is left on TOS, the second function is performed, after which the test on the first is repeated and so on), like in: 10[%0>][%.1-]! 10 is put on TOS; the test is performed onto the first function; read it as if: Is TOS greater than zero? If the answer is "yes" (flag true) perform the second function, which prints a copy of TOS, and decrement TOS itself; then perform the test again. It's very easy to implement a definite loop (like a for-next), in which a counter executes the cycle a fixed number of times: - one way is to use the stack, like in the two preceding examples; - the other is to use variables: it's sufficient to store a value into a variable and the limit into another, incrementing/decrementing the first after each cycle, and checking when it reaches the limit. An advice: to check for zero, one could use (if absolutely sure) only %, to test the copy of TOS (a zero is false, any other value is true); but if you are uncertain of the path your cycle is doing, or if your increment/decrement is not the unity, you may miss the zero, and the limit would never be met. For instance: 11[%][%.2-]! will last forever, because TOS will reach 1, then -1 and will never meet 0; In these cases, it is safer checking for a range, and not for a value, like: 11[%0>][%.2-]! Here we are testing for TOS > 0, so that it will fail when it reaches -1. 6.8 Notes about symmetry and symbols -------------------------------- Simmetry You may have already noticed that the design of the flow control commands is built around a particular symmetry. The following drawing shows this concept: 1 func | 2 funcs |------+-----------+-----------+ | bool | ? | ? | | | if | if/else | |------+-----------+-----------+ | no | ! | ! | | bool | repeat | while | |------+-----------+-----------+ (Table 6.5) Boolean functions (marked as 'bool') need at least one truth value on TOS. The other functions don't need a truth value on TOS to begin, but need a truth value on TOS to continue. Symbols I give you some tips to avoid confusion in using some double characters commands; first of all, remember that a comma (,) always means "store", while the at (@) symbol means "fetch" (or "execute", in case of function). Now, three couples of symbols are those for the indexed functions, for the array accessing and for PAD execution, here summarized: | command | stack notation | purpose | +----------+---------------------+------------------------------------------+ | #, | ( n m - ) | store n at position m of integer array | +----------+---------------------+------------------------------------------+ | #@ | ( m - n ) | fetch n from position m of integer array | +----------+---------------------+------------------------------------------+ | @, | ( v - ) | store function contained into v to index | +----------+---------------------+------------------------------------------+ | @@ | ( - x*n ) | execute function stored into index | +----------+---------------------+------------------------------------------+ | _, | ( "stream" v - ) | put PAD into var v as function | +----------+---------------------+------------------------------------------+ | _' | ( "stream" v - ) | put PAD into var v as a string | +----------+---------------------+------------------------------------------+ | _@ | ( "stream" - x*n ) | execute PAD as function | +----------+---------------------+------------------------------------------+ (Table 6.6) In the preceding schema, you can see the pattern: - the # symbol means "array" (read commands #, and #@ respectively as array-store and array-fetch) - the @ symbolizes the function index (read commands @, and @@ respectively as index-store and index-fetch) - the _ represents the PAD (read commands _, and _@ respectively as PAD-store [into a function] and PAD-fetch) There's a command that doesn't fit into this pattern: the comma-comma command (,,): it reverses a function into PAD, and in this sense it must be seen as the reverse of the _, command; the problem is it can also be called (as for the meaning) PAD-store, but to avoid confusion, I call it comma-comma, and base upon _, to build the contrary. 7 Installation ------------ Installing owl is very simple. Follow these steps: 7.1 Linux/UNIX (except Mac OS X) ---------------------------- as user, inside the unpacked directory of sources, type $ make at the prompt of your terminal, and then copy the owl executable to any point of your home directory contained into $PATH (you can also edit the Makefile to execute the install procedure to your home directory) and file owl.1 into your current man1 folder for man pages. To make owl available to any user, as root type: # make install after make and that's it. Edit Makefile to alter the destination paths at your will. Of course, if you are in the /etc/sudoers file, you can execute the same action as a normal user and not root, this way: $ sudo make install You will be requested of your password. 7.2 Mac OS X -------- You certainly know that Mac OS X users are of type common, administrative or root. Common user For what about the making process, as a common user, simply type: $ make inside the unpacked directory of sources from your Terminal.app (located into /Applications/Utility). To install, copy the executable to any location. To automatize the process and making it available to any user, the simplest way is typing the following commands as common user who knows an administrative password: $ make $ su $ sudo make install $ exit (I suppose that the Developer's package is installed, and that Makefile destinations are valid directories, but you can choose any other, modifying it; substitute with your own administrative user's name). OK. All done. Administrative user If you are an administrative user, simple type: $ make $ sudo make install (eventually alter Makefile for changing the destination paths). You will be requested of your password. That's done. There's no need to become root. 7.3 Windows ------- On Windows (any version), follow these steps: - get a C compiler environment (gcc or minGW32, or a GNUStep package that offers a complete building environment based on mingw32) - type 'make' inside the unpacked directory of sources from the Console (cmd.exe) or from the Dos Prompt Terminal and with the building environment into PATH, to compile (the Makefile must be present). - copy the executable where you want. You will use it from Console (cmd.exe) or Dos Prompt. Notice that under Windows THE LANGUAGE IS AFFECTED! Differences are: - the -t option is disabled - the system time functions (_t and its companions) are not available. This means that a command like _t won't return six values (only the t ASCII value), and a command like _th won't return the hour, but the ASCII code of t and h. - the ( command 'one-key' feature won't be enabled, but the command itself will work: only, you'll have to press the Return Key after the input char, and if you type other letters after the first, they will get lost. 7.4 Conclusions ----------- Use _OS to detect the underlying OS for the source file you are creating, in order to select these features usage, if you plan to develop portable source code. Once you can compile owl, any owl source should be interpreted correctly for any Operating System (with all the limitations for Windows we saw). I tested it under Linux (Fedora Core 4, Red Hat 9A), Mac OS X (10.4.x Tiger) and Windows XP (Sp2). 8 Bibliography ------------ [1] http://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Incompatibilities.html where at point 10.4, second comma, the following is stated about the integer int type limit, equal to 2^31 (the same can be applied to the long long int type limit 2^63 used by owl.): "-2147483648 is positive. This is because 2147483648 cannot fit in the type int, so (following the ISO C rules) its data type is unsigned long int. Negating this value yields 2147483648 again." [2] http://www.azillionmonkeys.com/qed/2scomp.html) where the fact exposed in [1] is seen in another perspective: "While mathematically we all know that negating a negative number will yield a positive number, it turns out that that is not the case for 2s complement representations. The trouble is with the value 0x80000000 (in 32 bits) or just MIN_INT. In 2s complement, negation is defined as complementing and adding 1. Here we find that negating this value just returns itself. Furthermore, this cannot be fixed by simply mapping this negation to some other value since we expect that -(-(x)) to be equal to x." 9 Finally... ---------- The draft of this document begun in textual form on May 1, 2005, while coding started on May 14, 2005. I hope you like this language and appreciate the work I've done on it. Feedback is anyway greatly expected, and your contributions too. Send any message (greetings, critics, bugs, suggestions, programs and anything you think important) to the address below. I am Antonio Maschio, and you can reach me at . Enjoy! It's GPL!