moved mark/resolve to the ealier file; added "compiler:cblock-start" and "compiler...
[urforth.git] / URFORTH.me
blobafcd07c5438d141483690618daa69818ea018db3
1 this is direct-threaded self-hosting x86 32-bit GNU/Linux Forth system.
3 UrForth level 1 doesn't need anything except UrForth itself to build the
4 system from the sources. there is a prebuilt binary in this repository
5 which can be used to bootstrap the system (with 'c.sh' shell script).
7 some notes about standards-compliance: i don't fuckin' care. pre-ANS
8 standards are too restrictive, and ANS standard is fubared with idiotic
9 "portability", and inability to get rid of obsolete concepts. this goes
10 up to 2012 (and then); just look at `FIND`! and fuck it, i don't expect
11 anybody (except me, of course) to use UrForth anyway.
13 also, about ANS "portability" crap: i strongly believe that default
14 architecture should be 32-bit 2-complement one, without any forced data
15 align rules. for non-conformant systems, an implementation should emulate
16 the abovementioned arch. this represents most architectures out there,
17 and if your arch is different, your Forth system should take care of that,
18 running standards-compliant code without any changes. and library authors
19 can avoid error-prone jumping through the hoops.
21 if you think that speed is more important than compatibility, there is
22 always an option to ignore any standards.
24 also, i'm not planning to create 64-bit versions of UrForth. "64-bitness"
25 is another thing i consider useless. and PIC code will not be supported.
28 more ANS idiocity: 2! and 2@ are storing 64-bit numbers as
29 big-dword-endian. i introduced "2!LE" and "2@LE", and left "standard"
30 words intact.
32 also, "cell" was another idiotic word choice. does "c@" operate on cells,
33 or on chars? i wonder if anybody there had even one working *brain* *cell*.
34 (got it? got a joke? it's a great joke!)
37 i added benchmark from BigForth. so far, UrForth is ~2.2 (2.1 with
38 debugger) times slower than BigForth on it. of course, benchmarks only
39 shows how good the system can pass a benchmark, but stil... it's not that
40 bad, considering that BigForth is subroutine-threaded (i.e. generates
41 native code), and somewhat optimised. also, it is only ~1.12 times slower
42 that unoptimised SPF4.
45 some advanced UrForth features, in no particular order:
47 * vocabularies has hastables for word names (256 bytes per vocabulary).
48   this makes searches ~50-100 times faster:
49     FORTH -- 799 words, 64 of 64 buckets used, 4 min items, 21 max items, average: 12 words per bucket
50   considering that each word first checked for valid hash and length,
51   the searcher usually does only one full string comparison.
52   in other words: vocabulary searches are lightning fast.
53   (ok, its not that advanced, because most Forth systems out there does
54   that, but hey, it's my book, written by my rules!)
56 * number prefixes:
57     $,#,0x,&h -- hex number (note that 2012 standard wants "#" for decimal)
58     %,0b,&b -- binary number
59     0o,&o -- octal number
60     0d,&d -- decimal number
62 * number postfixes:
63     nnnH -- hex
64     nnnO -- octal
65     nnnB -- binary (only for BASE<12)
67 * underscores in numbers are ignored:
68     0x8000_00_00 is a valid number
70 * extended word search:
71     you can use "a:b" to find word "b" in vocabulary "a".
72     of course, "a:b:c" and such are allowed too.
73     note that "colon access" will ignore "hidden" attribute. this is not a bug.
75 * UrForth has fully working BREAK and CONTINUE, they can be used in BEGIN and DO...LOOP.
76     also, they know about CASE, so you can use BREAK/CONTINUE in OF/OTHERWISE clauses.
78 * BEGIN loops can contain arbitrary number of WHILE/NOT-WHILE parts,
79   and they can be terminated with UNTIL/NOT-UNTIL even if WHILE is present.
80   AGAIN is allowed too, for any kind of BEGIN loop.
82 * there is IFNOT in addition to IF. also, there are +IF and -IF, to check for
83   positive and negative numbers respectively. zero is neither negative, nor
84   positive.
86 * segfault handler will show you stack dump and backtrace.
88 * vocabularies supports public and hidden words. it is possible to create
89   "nested" vocabulary, which will see all parent's words (including hidden ones).
91 * multiline comments: normal (* ... *), and nested (( ... ))
92   nested multiline comment allows other nested comments
94 * x86 assembler with defered plug-in interfaces for memory r/w and label manager
95   (can be used to create metacompilers, or even standalone assemblers).
96   it is using normal intel syntax, not yoda-style. also, you don't need to
97   separate operands with spaces, because assembler does its own input stream
98   parsing.
100 * there are `[:` and `;]` to create cblocks. this feature can be used like this:
101     : foreach-do ( cfa -- )  10 0 do i over execute loop drop ;
102     : a  [: . cr ;] foreach-do ;
103   internally, it compiles header-less word, and leaves its CFA on the stack.
105 * cblocks can be used in interpreter too, i.e. you can type this at REPL:
106     [: 10 0 do i . cr loop ;] execute
107   and it will work. and even this will work:
108     [: ." hey!" cr [: 65 emit cr ;] execute ;] execute
109   note that you cannot assign such cblocks to DEFERed words, because they
110   will not live long enough.
111   also, this feature is not really planned, it is just a side-effect of cblocks
112   implementation. do not rely on it.
114 * internally, there is DP-TEMP variable. when it is 0, the normal DP is used
115   for HERE. but when it is non-zero, all HERE-based words will use it instead.
116   this is used to make cblocks working in interpreter, for example. no words
117   except "HERE" and "N-ALLOT" are accessing "DP" directly.
119 * "OVERRIDE" word can be used to override Forth words with other Forth words.
120   it works like this:
121     : newdot  ( n old-xtoken )
122       check-some-condition if
123         OVERRIDE-EXECUTE
124       else
125         2drop
126       endif
127     ;
128     OVERRIDE . newdot
129    or
130     ' . ' newdot (OVERRIDE)
132   note that overriden word can be called as usual, and override forces even
133   previously compiled words to call `newdot`. also note that `newdot` cannot
134   be called as normal forth word anymore (no checks are made, it will just
135   make your system unusable due to UB).
137   currently, there is no way to "unoverride" the word. it may be added later.
139   also, remember that "old-xtoken" cannot be called with "EXECUTE". but you
140   can freely pass "old-xtoken" around and call it with "OVERRIDE-EXECUTE" at
141   any moment, and at any place.
143   this can be used to create things like metacompilers, for example, without
144   duplicating all compiler word definitions again.
146   if you will override already overriden word, old override will be replaced
147   with the new one (i.e. the overrides are not chained).
149   you can chain overrides like this, if you want to:
150     0 value (prev-ovr)  (hidden)
151     : ovr-new  ( ... xtoken -- )
152       (prev-ovr) ?drop override-execute
153     ;
154     get-override . to (prev-ovr)
155     override . ovr-new
157   note that this feature is highly experimental, and may change/disappear.
159 * the kernel includes "scattered colon" feature (invented by M. Gassanenko).
160   most of initialisation and other chained things are implemented with it.
161   also, there are interpreter and "to" scattered colon hooks.
162   see "samples/scolon.f" for example code.
164 * "REPLACE" word can be used to perform system-wide word replacement:
165     replace oldword newword
166   WARNING! you can replace any word with any other word (including constants,
167   code words, and so on, on both sides; the desired effect is up to you). i.e.
168   this will work:
169     69 constant fuck
170     : hell 666 ;
171     replace fuck hell
172     fuck 666 = .  ( true )
173   note that aliases may be optimised to the original words, so replacing an alias
174   may not work for compiled code.
176 * you can set dstack/rstack value with POKE/RPOKE. this is complementary to
177   PICK/RPICK (and i aliased those to PEEK/RPEEK).
178   usage is: `value index POKE`, where `index` is the same as in PICK.
180 * there is FOR ... ENDFOR that accepts limit, and iterates [0..limit). it is
181   safe to use with negative or zero limit -- the loop will be skipped in this case.
182   i.e. `3 FOR I . ENDFOR` will print "0 1 2", and `-1 FOR I . ENDFOR` will print
183   nothing.
185 * interpreter extension mechanics:
186   there are two scattered colon words:
187     (INTERPRET-WFIND)      ( addr count -- cfa 1 // cfa -1 // addr count false )
188     (INTERPRET-NOT-FOUND)  ( addr count -- true // addr count false )
190   "(interpret-wfind)" will be called to find a word.
191   return `1` if cfa is immediate, `-1` if it is a normal word.
192   if your chain handler succeeded, you MUST EXIT from it!
193   you can leave various data on the stack if your handler returned immediate word.
195   "(interpret-not-found)" will be called when a word cannot be found, and cannot
196   be converted to a number.
197   if your chain handler succeeded, you MUST EXIT from it!
201 some notable differences from ANS:
203 * number parser does not understand decimal dot, nor floating exponent; i.e. you cannot
204   directly input double numbers or floats. this is because i don't see much use in
205   that, and you can write your own prefix words if you need to (as fp did with "f#").
206 * TRUE is 1, not -1; there are LOGAND and LOGOR; NOT is logical, bitwise not is BITNOT.
207 * TIB is still there, no ANS SOURCE and such.
208 * TIB size is in variable #TIB; there's no need to finish TIB with any special byte. yet
209   the parser will stop if it will see zero byte in TIB.
210 * current tib line is in variable TIB-LINE# (if it is 0, no line counting and debug info).
211 * do not read TIB manually, use "tib-peekch" and "tib-getch".
212 * DOES> words use extra bytes after PFA.
213 * there is [COMPILE], and no CHAR or ASCII (use [CHAR] instead).
214 * [CHAR] will error on any string that is not one-char.
215 * use WFIND ( addr count -- cfa -1 // cfa 1 // false ) instead of idiotic FIND.
216 * COUNT expects cell-counted string; for byte-counted, use BCOUNT.
217 * WORD returns cell-counted string (at HERE) (but don't use it, use PARSE and PARSE-NAME).
218 * there is N-ALLOT ( size -- start-addr ) low-level word, which is used by ALLOT and
219   others. it returns starting address of the allocated dictionary memory (which can
220   be in transient DP-TEMP).
221 * i don't fuckin' know what "address unit" is, and why it is necessary. so MOVE always
222   works with bytes.
223 * FIG-style "CFA->NFA" and such are still there.
224 * S" always unescapes string, because i see no reason to not do it. also, terminating
225   zero byte is always there (but it is not included in count).
226   S" will use byte-counted string if it can (with a different LIT word).
227 * ." cannot print strings longer than 255 chars ('cmon!).
228 * there is FIG-style VAR and DEFERED is FIG-style (VARIABLE and DEFER are ANS)
229 * PAD is not relative to HERE, it resides in separate memory area.
230 * floating point words are supported only partially, via FPU+FPU stack; printing and
231   parsing of floating point numbers are very inexact, and cannot be used for round-trips.
232   this is something i'm planning to fix eventually (prolly by porting Ryu and plan9 parser).
233   note: 32-bit floats seems to round-trip under the default FPU mode, but it's still better
234   to not rely on that.
235 * do not even try that ANS-allowed idiocity like "BEGIN ... WHILE ... UNTIL ... THEN".
236   this is hard to read, and plainly stupid. in UrForth, you can have as much "WHILE"
237   (or "NOT-WHILE") secions as you want to, and you can finish "BEGIN" with any of
238   "REPEAT", "AGAIN", "UNTIL". there is no need for extra "THEN" (and the compiler will
239   complain if you'll try to do such thing). also, there are "BREAK" and "CONTINUE" words.
240 * `ABORT"` is unconditional abort. use `?ABORT"` instead -- this is more logical, and is
241   in line with `?ERROR`. "use-lib: ans" will change that.
242 * ALIAS will copy source word attributes ("hidden" and "immediate"). not copying attributes
243   is another standard idiocity, and a source of bugs. i've never ever encountered a case
244   where i don't want an alias to not be immediate when a source word is immediate, for example.
245   "use-lib: ans" will change that.
249 FAQ ;-)
251 Q: i am calling functions from .so, and they are segfaulting at random!
252 it doesn't happen with C code, UrForth has a bug there!
254 A: nope. what is happening is that your system is broken, and doesn't
255 follow ABI. 32-bit ABI doesn't require the stack to be aligned in any
256 particluar way (except being dword-aligned), but modern GCC not only
257 aligns the stack at 16 bytes, but generates code that expects the stack
258 to be always aligned like that. it is a violation of ABI, and a bug in
259 GCC. rebuild your system with "-mstackrealign" GCC flag to fix it.
261 Q: but everybody else is happy to do what GCC du..evelopers command them
262 to do! why can't you simply add stack aligning code to UrForth?!
264 A: because i see no reason to workaround GCC bugs in my code. the longer
265 we will tolerate GCC ABI breakage, the longer it will last.
267 Q: what the fuck with your assembler code? what is "ld", "jr", "jp",
268 "cp", and other shit?
270 A: i am sorry. i used to write for Z80, and i am too lazy to switch
271 mnemonics. conventional x86 mnemonics are supported too, so you can use
272 whichever you like. consult Z80 manual to understand the shit i wrote, if
273 you really need to.
276 have fun, and happy hacking
277 Ketmar Dark // Invisible Vector