home *** CD-ROM | disk | FTP | other *** search
-
- The FALSE Programming Language
-
- by Wouter van Oortmerssen
-
- Manual
-
-
- WHAT'S NEW in v1.1
- - one bug fix
- - other example sources (mainly written by Eelko, in the "other" dir.)
- - Portable False Interpreter/Debugger!
- (read comments in the source for more infos)
-
-
- +-------------------------------+
- | Introduction: |
- +-------------------------------+
-
- The language FALSE and it's compiler were designed for only two reasons:
- - building a working compiler in just 1k (!)
- - designing a language that looks cryptic and fuzzy (in the APL tradition)
-
- the result is a language that is quite powerfull (for it's size).
- It's a Forth type language with lambda abstraction and lots of other goodies.
- I named the language after my favourite truthvalue.
-
- NOTE: a) the compiler as well as the generated code need kickstart v37+
- b) You're strongly advised to read this entire manual before
- trying to operate the compiler.
-
- +-------------------------------+
- | The implementation: |
- +-------------------------------+
-
- To compile a FALSE program, type "false" followed by the source-code
- name, like:
-
- 1> false helloworld.f
-
- the compiler produces an executable called "a.out" in the same dir:
-
- 1> a.out
- Hello, World!
- 1>
-
- To squeeze the real compilation functions for all language elements in
- 1024 bytes, some things had to go: there are no error messages, and even
- worse: there are no syntax error checks. Luckily, the language is
- designed so that it's hard to make compile-time errors.
-
- the compiler only signals an error in the following events:
- - it could not allocate memory
- - it could not read the source file
- - it could not write the executable
- - it could not open dos.library (very unlikely)
- - it found a symbol in the source, which isn't part of the language.
-
- an error is signalled by returning value 10 instead of 0, it is
- therefore wise to have your cli-prompt display return values ("%R")
-
- note: the compiler will start acting weird as soon as you try
- to compile sources or produce executables >32k
-
- Working with FALSE
- ------------------
- It is actually possible to write small utilities and stuff in FALSE,
- and quite powerfull too once you see how to use stacks and lambda
- functions etc. However, with a minimal compiler like this it's hard to
- find errors, and I suppose you really have to be a Forth hacker or a
- hardcore programmer to get the most of it.
-
- Helloworld in FALSE:
- --------------------
-
- "Hello, World!
- "
-
- (yes, that's all...).
- And this is the fac() function definition in FALSE:
-
- [$1=$[\%1\]?~[$1-f;!*]?]f:
-
- (fuzzy eh? we'll explain that later...)
-
-
- +-------------------------------+
- | FALSE: The Language. |
- +-------------------------------+
-
- Format of the language:
- -----------------------
- FALSE sources are totally free-format, i.e you may have any number
- of tabs/spaces/lf's between two symbols. comments start with "{",
- end with "}" and may not be nested.
-
-
- evaluation:
- -----------
- FALSE inherits its way of evaluating expressions from Forth, so it
- really helps if you know that language, but for those who don't:
-
- All elements in the language are defined by what they push on and/or
- pop from the stack. for example, a number like "1" or "100" simply
- pushes it's own value on the stack. An operator like "+" takes the
- two top elements of the stack, adds them, and pushes back the result:
-
- 1 2 + 4 * { (1+2)*4 }
-
- the result of this expression is "12". We will use the notation
- (<pops>-<pushes>) to signify what a function does, so "1" does (-num)
- and "+" does (n1,n2-result)
-
- complex expressions will keep lots of intermediate results on the stack,
- so mostly there's no need for local variables. FALSE doesn't even
- have expressions or statements; more likely a program is one stream
- of symbols that manipulate the stack. It's very helpfull when you
- can imagine what the stack looks like in a particular part of the program
- when programming.
-
-
- elementary functions:
- ---------------------
-
- available are:
-
- "+" "-" "*" "/" "_"
-
- these function as usual. "_" is the unary minus.
-
-
- "=" ">"
-
- these result in 0 (false) or -1 (true)
-
- unequal is "=~", and smaller than etc. can be made by swapping arguments
- and/or using "~"
-
- example: a;1_=~ { a<>-1 }
-
-
- "&" "|" "~"
-
- "and", "or" and "not", as usual.
-
- example: a;0>a;99>~& { (a>0) and (a<100) }
-
-
- values:
- -------
-
- values are either integers like discussed before ("1", "100" etc.),
- or characters precede by a quote: 'A (equals 65) (do not mix up with
- backquote "`" !)
- note that the 1k parser only parses integers up to 320000, it uses
- full 32bit representation for them, however.
-
-
- global variables:
- -----------------
- variables to store values are less needed in FALSE than in other
- languages. in FALSE they are used mostly for functions, explained
- below.
-
- a variable is a character "a" to "z" (just these).
- ":" is the assignment function, and ";" is contrary: it gets the
- variable's value:
-
- 1a: { a:=1 }
- a;1+b: { b:=a+1 }
-
- i.e: "a;" is used where in other languages you would just write "a"
-
- all variables (and also stack-items) are 32bits.
-
- lambda functions:
- -----------------
- a FALSE lambda function is a piece of code between []. for example:
-
- [1+]
-
- is a function that add 1 to it's argument. A function is really defined
- by what it takes from the stack (in this case the first arg to "+"), and
- what it puts back, just like builtin functions. Note that FALSE lambda
- functions are not restricted to just one return value.
-
- what a [] expression really does, is push the function. this means in
- practise that it can be given to yet another function as argument etc.,
- just like in functional languages. The symbol "!" is called "apply",
- and applies a function to it's arguments, for example:
-
- 2[1+]!
-
- would result in "3"
- This wouldn't make much sense, since what you really want is define the
- function once, and then use it all-over. this is easy:
-
- [1+]i:
-
- this defines the function "i" (actually, it assign the function to "i"),
- so that it can be used simply by applying "i" to it's arguments:
-
- 2i;!
-
- WARNING: as with all other elements in FALSE, but even more important
- with functions: the 1k compiler does not check if symbols like
- "!" really get a function as argument (so "1!" means trouble),
- the compiler may even crash if you don't balance your [ and ].
-
-
- stack functions:
- ----------------
-
- "$" (x-x,x) dup: duplicate topmost stackitem
- "%" (x-) drop: delete topmost stack item
- "\" (x1,x2-x2,x1) swap: swap to topmost stack-items.
- "@" (x,x1,x2-x1,x2,x) rot: rotate 3rd stack item to top.
- "ø" (n-x) pick: copy n-th item to top (0ø equals $)
-
- examples:
-
- 1$ equals 1 1
- 1 2% equals 1
- 1 2\ equals 2 1
- 1 2 3@ equals 2 3 1
- 7 8 9 2ø equals 7 8 9 7
-
-
- control structure:
- ------------------
- FALSE only has an IF and a WHILE.
- if is "?", and looks like this: (bool,fun-). example:
-
- a;1=["hello!"]? { if a=1 then print "hello!" }
-
- the first argument is a boolean value, the second the lambda function
- to be executed (see below for "")
- there's no "else", so you'll have to mimic this with a second "?".
- this can be easily done by copying the truthvalue:
-
- a;1=$["true"]?~["false"]?
-
- after the first "?" (wether it's executed or not), a copy of the truthvalue
- is still on the stack, and we negate it for the else part.
- Beware that if the first "if" needs arguments on the stack from before
- the boolean expression, it's top is still the truthvalue.
-
- While is a "#", and gets two lambda functions as args, one that results in
- a boolean, and the second as body:
-
- [a;1=][2f;!]# { while a=1 do f(2) }
-
- note that with while, if and lambda's, you can build virtually
- any other control structure.
-
-
- Input/Output:
- -------------
- watch out: all these are BUFFERED.
-
- - strings printing: strings simply print themselves. Special: strings in FALSE
- may contain <lf>'s, that explains why in the helloworld program, the second
- " is on the next line:
-
- "Hello, World!
- "
-
- - integers: "." prints the topmost stack item as integer value:
-
- 123. { prints string "123" on console }
-
- - characters: ","
-
- 65, { prints "A" }
-
- - reading a character from stdin: "^"
-
- ^ { top stack is char read }
-
- - flush: "ß"
- when stdin and stdout are different (i.e. you started your compiled
- FALSE program with <infile >outfile you will hardly need to flush, however,
- if you both use "^" and the output operations on the same console,
- you may need to flush between input and output.
- "ß" flushes both input and output.
-
- ß[^$1_=~][,]# { while c:=getc()<>EOF do putc(c) }
-
- for example, above program copies input to output until eof,
- so no flushing is needed after every read when used with two
- files, however:
-
- "press return:"ß^%ß"continuing..."
-
- it is, since we get input on the same console as the output.
-
-
- +-------------------------------+
- | Example programming |
- +-------------------------------+
-
- How the $#%! am I going to write a decent program with all this, you may ask.
- Well, the first barrier one has to take into account, is that FALSE doesn't
- support any amiga-specific programming, some io-functions is as far as
- it gets. However, with those, you can create some stunningly compact
- utilities, for example the FALSE version of the "copy" command we saw above,
- in just 13 bytes!
-
- ß[^$1_=~][,]#
-
- ok, what happens: first recognise the four main parts in this program,
- we have a flush, then two arguments and then a while. The first []
- function is supposed to deliver the boolean for the while: it reads
- a character, duplicates it, the compares it it with -1 (EOF). at the end
- of this function, we do not only have a boolean, but also an extra copy
- of the character we read. This immediately demonstrates a powerfull
- feature of FALSE: we can have any number of interim-results on the
- stack. the body of the while loop [,] just prints the character to
- stdout.
- Note that if the body is not executed, we leave with a non-empty stack.
- This is no problem at the end of a program, however, doing this within an
- iteration would be fatal.
-
-
- another example of FALSE programming: the fac() function.
-
- [$1=$[\%1\]?~[$1-f;!*]?]f:
-
- thus we call fac(6) (=720) like:
-
- 6f;!
-
- no range checking is done by the "f" function, that is what
- is done by the fac.f example program.
-
- Well, how does it work? (does it?) First recognise the f;! within
- the function implementation: that's the recursion. Let us recall what
- fac() looks like in a hypotheticall procedural/functional programming
- language:
-
- fac(n) = if n=1 then 1 else n*fac(n-1)
-
- our FALSE code goes just along these lines, only we use two "if"'s (hence
- the two [] blocks) insteas of one if-then-else.
- we start with (n-) on the stack:
-
- $1=$
-
- duplicate the n, and compare it with 1, and leave a second truthvalue (t),
- thus: (n,t,t-)
-
- [\%1\]?
-
- first push the [], and after the "if" (=?) we have (n,t-). we won't
- be needing the lower n anymore, so we swap and drop. then we push the
- final result "1", and swap it below the truthvalue for the second "if".
- (1,t-)
-
- ~[$1-f;!*]?
-
- we first have to negate the truthvalue, because this is the else part.
- in the "if"-body, we have just (n-), and we add a "n-1" to that as argument
- for the recusive call. after f;! we have (n,r-) (r is result of the call),
- and we simply multiply the two together as result of the whole.
-
- this may look all awfully complicated, but infact, it isn't. it's
- just a very different style of programming. once you fully understand
- it's power, you won't want to live without it :-)
-
- if by now you haven't understood zip of how FALSE works, this probably
- isn't the language for you. however, if you got the slightest feeling
- that some things are getting clear to you, try understanding the examples,
- and your on your way of becoming a real FALSE programmer ! :-). however,
- the examples are not heavily commented, as that is considered bad-taste
- in FALSE (see some section below).
-
-
- +-------------------------------+
- | FALSE wizards corner |
- +-------------------------------+
-
- "Inline assembly" in FALSE:
- -------------------------
-
- one topic has been kept undiscussed (on purpose), and it's the
- possibility to add assembly code to a FALSE program, to allow it to
- be extended with custom functions.
-
- syntax: <integer>`
-
- any integer value 0..65535 folowed by a backquote. an expression like
- this causes the a 16bit value to be put directly into the code.
- A series of backquoted values may allows you a primitive form of
- inline assembly. for example:
-
- [8221`29184`9336`4`50510`20142`65338`50510`11008`]a: { allocmem (size-mem) }
- [8221`8797`9336`4`50510`20142`65326`50510`]f: { freemem (mem,size-) }
-
- are two assembly functions that allow you to allocate memory
- from within FALSE (see example alloc.f)
-
- register conventions:
- when writing assembly code for use with FALSE, use following registers:
-
- A6 = dosbase
- A5 = evaluation stack. use MOVE.L (A5)+,D0 to read a paramenter into D0,
- and MOVE.L D0,-(A5) to write one.
- A4 = variables. 0(A4) = a, 4(A4) = b etc.
- D6 = stdout
- D5 = stdin
-
- example code for allocmem/freemem above:
-
- alloc: move.l (a5)+,d0
- moveq #0,d1
- move.l 4.w,a2
- exg a2,a6 ; we need to restore dosbase later.
- jsr -198(a6)
- exg a2,a6
- move.l d0,-(a5) ; no rts, that's done by []
-
- free: move.l (a5)+,d0 ; second argument first!
- move.l (a5)+,a1
- move.l 4.w,a2
- exg a2,a6
- jsr -210(a6)
- exg a2,a6
-
-
- peek/poke:
- ----------
-
- ":" and ";" are operators to read and write variables, but they can be
- (mis-)used to do arbitrary peek and poking, even array-indexing!
- (see vcheck.f for an example: we read execbase)
-
- array indexing and structure reading:
- if p is a pointer to an array/structure, then:
-
- p;<index>+;
-
- reads p[<index>].
-
- unfortunately, this way you can only read 32bit values.
-
- cli arguments:
- --------------
-
- reading files is mostly done with redirection on the commandline, however,
- for future extensability, a pointer to the command-line arguments is
- passed in var a. see also "command line tips" below.
-
- stack:
- ------
-
- you can use the stack as a buffer, and reverse-indexing the values
- on it with ø. however, the FALSE stack is the lower-half of the normal
- amigados stack, and thus normally only 2k. You can write programs
- that need arbitrarily large stack-buffers by increasing the stack
- size before running.
-
- command line tips:
- ------------------
-
- - make sure you write an input redirection when testing some
- programs: if the program simply does "nothing", than the
- computer is not hung, but it's simply waiting for input.
- - if you do not write a flush (ß) at the start of a program that
- processes the input to the output, you will get a <lf> as
- first input: this is actually the commandline. example:
-
- a.out blabla <in >out
-
- then a.out will first read "blabla" as a line, then the contents
- of "in".
-
-
- good style:
- -----------
-
- programming in FALSE has a certain kind of taste: it's not easy, but
- when it works, it works good, and the resulting sources look great.
- Therefore, it may be tempting to "indent" while-loops in larger
- programs, but remember:
- - indentation, spacing and comments are for wimps :-)
- - real FALSE programmmers:
- - write dense code
- - write only very "global" comments.
- - use the stack intensively, and thus dislike to use variables
- for other purposes than function definitions.
-
-
-
- +---------------------------------------+
- | FALSE language overview. |
- +---------------------------------------+
-
- syntax: pops: pushes: example:
- -->top -->top
- --------------- --------------- --------------- -------------------------------
-
- {comment} - - { this is a comment }
- [code] - function [1+] { (lambda (x) (+ x 1)) }
- a .. z - varadr a { use a: or a; }
- integer - value 1
- 'char - value 'A { 65 }
- num` - - 0` { emitword(0) }
-
- : n,varadr - 1a: { a:=1 }
- ; varadr varvalue a; { a }
- ! function - f;! { f() }
-
- + n1,n1 n1+n2 1 2+ { 1+2 }
- - n1,n2 n1-n2 1 2-
- * n1,n2 n1*n2 1 2*
- / n1,n2 n1/n2 1 2/
- _ n -n 1_ { -1 }
-
- = n1,n1 n1=n2 1 2=~ { 1<>2 }
- > n1,n2 n1>n2 1 2>
-
- & n1,n2 n1 and n2 1 2& { 1 and 2 }
- | n1,n2 n1 or n2 1 2|
- ~ n not n 0~ { -1,TRUE }
-
- $ n n,n 1$ { dupl. top stack }
- % n - 1% { del. top stack }
- \ n1,n2 n2,n1 1 2\ { swap }
- @ n,n1,n2 n1,n2,n 1 2 3@ { rot }
- ø (alt-o) n v 1 2 1ø { pick }
-
-
- ? bool,fun - a;2=[1f;!]?
- { if a=2 then f(1) }
- # boolf,fun - 1[$100<][1+]#
- { while a<100 do a:=a+1 }
-
- . n - 1. { printnum(1) }
- "string" - - "hi!" { printstr("hi!") }
- , ch - 10, { putc(10) }
- ^ - ch ^ { getc() }
- ß (alt-s) - - ß { flush() }
-
-
-
- +-------------------------------+
- | additional infos |
- +-------------------------------+
-
- FALSE was created for fun, just to see how small a compiler I could
- write while still compiling a relatively powerfull language.
- The result is even better than I thought: it's great fun to
- program in FALSE, and it looks even better.
-
- about the compiler source: note that throughout the program ALL
- variables reside in registers without saving on the stack!
- please: don't come and tell me you found a way to reduce
- the size of the executable by 4 more bytes... I think it's small
- enough as it is now. If you enjoy compilers just because of
- their lenght, make sure you get a look at the "brainfuck"
- language by Urban Mueller in less than 256 bytes!
-
- Donations? who would want to give something for a program that
- has the size of a bootblock virus? anyway, the only thing I'd
- like to receive is large and complex FALSE sources (of working
- applications, of course). Please always put a comment at the
- top of your source, otherwise you'll give me a hard time guessing
- if it's FALSE or uuencoded. don't ask me to debug your code, as
- understanding other peoples FALSE programs is horrible.
-
- If you want to contact me:
-
-
- Wouter van Oortmerssen ($#%!)
- Levendaal 87
- 2311 JG Leiden
- HOLLAND
-
- or better if you have access to Email:
-
- Wouter@alf.let.uva.nl (E/FALSE etc. programming support)
- Wouter@mars.let.uva.nl (personal)
- Oortmers@gene.fwi.uva.nl (other)
-
-