Provide initial preForth, simpleForth, Forth
authorUlrich Hoffmann <uho@xlerb.de>
Thu, 5 Apr 2018 13:57:30 +0000 (15:57 +0200)
committerUlrich Hoffmann <uho@xlerb.de>
Thu, 5 Apr 2018 13:57:30 +0000 (15:57 +0200)
21 files changed:
README.md [new file with mode: 0644]
preForth/Dockerfile [new file with mode: 0644]
preForth/Makefile [new file with mode: 0644]
preForth/borrow.fs [new file with mode: 0644]
preForth/forth.simple [new file with mode: 0644]
preForth/load-C-preForth.fs [new file with mode: 0644]
preForth/load-i386-preForth.fs [new file with mode: 0644]
preForth/load-preForth.fs [new file with mode: 0644]
preForth/load-symbolic-preForth.fs [new file with mode: 0644]
preForth/preForth-C-backend.pre [new file with mode: 0644]
preForth/preForth-C-rts.pre [new file with mode: 0644]
preForth/preForth-i386-backend.pre [new file with mode: 0644]
preForth/preForth-i386-rts.pre [new file with mode: 0644]
preForth/preForth-rts.pre [new file with mode: 0644]
preForth/preForth-symbolic-backend.pre [new file with mode: 0644]
preForth/preForth-symbolic-rts.pre [new file with mode: 0644]
preForth/preForth.pre [new file with mode: 0644]
preForth/simpleForth-i386-backend.pre [new file with mode: 0644]
preForth/simpleForth-i386-rts.simple [new file with mode: 0644]
preForth/simpleForth-rts.simple [new file with mode: 0644]
preForth/simpleForth.pre [new file with mode: 0644]

diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..f57544b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,81 @@
+# Bootstrapping Forth
+
+## preForth
+
+preForth is a minimal non-interactive Forth kernel that can bootstrap itself and can be used as an easy-to-port basis for a full Forth implementation.
+
+preForth feels like Forth - it's mainly a sublanguage of ANS-Forth - but is significantly reduced in its capabilities.
+
+### Features: minimal control structures, no immediate words, strings on stack, few primitives
+
+just
+
+- Stack
+- Returnstack
+- Only ?exit and recursion as control structures
+- :-definitions
+- optional tail call optimization
+- IO via KEY/EMIT
+- signed single cell decimal numbers (0-9)+
+- character constants via 'c'-notation
+- output single cell decimal numbers
+
+and
+
+- no immediate words, i.e.
+- no control structures IF ELSE THEN BEGIN WHILE REPEAT UNTIL
+- no defining words
+- no DOES>
+- no memory @ ! CMOVE ALLOT ,
+- no pictured numeric output
+- no input stream
+- no state
+- no base
+- no dictionary, no EXECUTE, not EVALUATE
+- no CATCH and THROW
+- no error handling
+
+### Prerequisites:
+
+  Just 13 primitives: emit key dup swap drop 0< ?exit >r r> - nest unnest lit
+
+## simpleForth
+
+simpleForth is an extension to preForth built using preForth. It is still non-interactive but adds 
+
+- control structures IF ELSE THEN BEGIN WHILE REPEAT UNTIL
+- definitions with and without headers in generated code
+- memory: @ ! c@ c! allot c, , 
+- variable, constants
+- ['] execute
+- immediate definitions
+
+## Forth
+
+Forth is a simple interactive Forth system built using simpleForth.
+Forth is open ended and has a yet incomplete set of features. Work in progress.
+
+
+# How to use:
+
+An i386-Backend (32Bit) indirect threaded code implementation based on [FASM](https://flatassembler.net/) is pre-configured.
+PreForth initially bootstraps on with [gforth](https://www.gnu.org/software/gforth/) or [swiftForth](https://www.forth.com/swiftforth/). 
+You'll need one of these for the first bootstrap.
+
+    cd preForth
+    make
+
+This will successively compile preForth, simpleForth, then Forth.
+
+If successful issue
+
+    $ ./Forth
+
+    Forth 1.2.0
+
+    last * warm cold empty patch minor major banner quit restart REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF ; : constant variable header cmove compile, , allot here dp +! clearstack interpret parse-name \ .( ( parse (interpreters ?word (compilers ,word immediate !flags @flags or and #immediate ] [ interpreters compilers handlers ,'x' ?'x' ,# ?# scan skip source /string >in query #tib tib accept min words .name l>interp l>name l>flags type count cell+ cells find-name .s prefix? compare 2dup 2drop rot off on ?dup + space bl cr . u. negate > 1- nip = 0= pick 1+ < over depth execute c! ! c@ @ ?branch branch lit exit unnest - r> >r ?exit 0< drop swap dup key emit bye 
+
+Inspect sources and generated files.
+
+*Have fun. May the Forth be with you.*
+
diff --git a/preForth/Dockerfile b/preForth/Dockerfile
new file mode 100644 (file)
index 0000000..23e9085
--- /dev/null
@@ -0,0 +1,31 @@
+from debian
+
+run dpkg --add-architecture i386
+
+run apt-get -y update && apt-get -y upgrade
+run apt-get -y install fasm gforth make gcc:i386
+
+run mkdir preForth
+workdir preForth 
+
+copy Makefile /preForth/Makefile
+copy preForth.pre /preForth/preForth.pre
+copy preForth-i386-backend.pre /preForth/preForth-i386-backend.pre
+copy preForth-i386-rts.pre /preForth/preForth-i386-rts.pre
+copy preForth-rts.pre /preForth/preForth-rts.pre
+
+copy borrow.fs /preForth/borrow.fs
+copy load-preForth.fs /preForth/load-preForth.fs
+copy load-i386-preForth.fs /preForth/load-i386-preForth.fs
+
+copy simpleForth.pre /preForth/simpleForth.pre
+copy simpleForth-i386-backend.pre /preForth/simpleForth-i386-backend.pre
+copy simpleForth-i386-rts.simple /preForth/simpleForth-i386-rts.simple
+copy simpleForth-rts.simple /preForth/simpleForth-rts.simple
+
+copy simpleForthDemo.simple /preForth/simpleForthDemo.simple
+
+run make bootstrap
+run make simpleForthDemo
+run ./simpleForthDemo
+
diff --git a/preForth/Makefile b/preForth/Makefile
new file mode 100644 (file)
index 0000000..02a90e1
--- /dev/null
@@ -0,0 +1,132 @@
+# Makefile for preForth
+#
+# make bootstrap should produce two identical files: preForth1.asm and preForth.asm 
+# or preForth1.c and preForth.c
+
+
+# Set PLATFORM to the intended preForth platform
+# ------------------------------------------------------------------------
+# PLATFORM=symbolic
+PLATFORM=i386
+# PLATFORM=C
+# ------------------------------------------------------------------------
+
+
+# Set HOSTFORTH to the Forth system that generates the initial preForth
+# ------------------------------------------------------------------------
+HOSTFORTH=gforth
+# HOSTFORTH=sf   # SwiftForth >3.7
+# ------------------------------------------------------------------------
+
+.PHONY=all
+all: preForth simpleForth forth
+
+.PHONY=test
+test: preForth.pre preForth-$(PLATFORM)-backend.pre load-$(PLATFORM)-preForth.fs 
+       cat preForth-$(PLATFORM)-backend.pre simpleForth.pre | $(HOSTFORTH) load-$(PLATFORM)-preForth.fs 
+
+# preForth connected to stdin - output to stdout
+.PHONY=visible-bootstrap
+visible-bootstrap: preForth preForth-$(PLATFORM)-backend.pre preForth.pre 
+       cat preForth-$(PLATFORM)-backend.pre preForth.pre | ./preForth
+
+# ------------------------------------------------------------------------
+# i386 version MacOS and Linux
+# ------------------------------------------------------------------------
+ifeq ($(PLATFORM),C)
+  UNIXFLAVOUR=stdC
+  EXT=c
+else
+  UNIXFLAVOUR=$(shell uname -s)
+  EXT=asm
+endif
+
+# preForth connected to stdin - output to preForth.asm
+preForth.asm: preForth.pre preForth-$(PLATFORM)-backend.pre load-$(PLATFORM)-preForth.fs
+       cat preForth-$(PLATFORM)-rts.pre preForth-rts.pre preForth-$(PLATFORM)-backend.pre preForth.pre \
+           | $(HOSTFORTH) load-$(PLATFORM)-preForth.fs >preForth.asm
+
+preForth: preForth.$(UNIXFLAVOUR)
+       cp preForth.$(UNIXFLAVOUR) preForth
+
+%.asm: %.pre preForth preForth-$(PLATFORM)-rts.pre preForth-rts.pre
+       cat preForth-$(PLATFORM)-rts.pre preForth-rts.pre $< | ./preForth >$@
+
+%: %.$(UNIXFLAVOUR)
+       cp $< $@
+
+# assemble and link executable on linux
+%.Linux: %.asm
+       fasm $< $@.o
+       ld -arch i386 -o $@ \
+          -dynamic-linker /lib32/ld-linux.so.2 \
+          /usr/lib/i386-linux-gnu/crt1.o /usr/lib/i386-linux-gnu/crti.o \
+          $@.o \
+          -lc /usr/lib/i386-linux-gnu/crtn.o
+       rm $@.o
+
+# assemble and link executable on MacOS
+%.Darwin: %.asm
+       fasm $< $@.o
+       objconv -fmacho32 -nu $@.o $@_m.o
+       ld -arch i386 -macosx_version_min 10.6 -o $@ \
+         $@_m.o /usr/lib/crt1.o /usr/lib/libc.dylib
+       # rm $@.o $@_m.o
+
+# run preForth on its own source code to perform a bootstrap 
+# should produce identical results
+bootstrap: preForth preForth-$(PLATFORM)-backend.pre preForth.pre preForth.$(EXT)
+       cat preForth-$(PLATFORM)-rts.pre preForth-rts.pre preForth-$(PLATFORM)-backend.pre preForth.pre\
+           | ./preForth >preForth1.$(EXT)
+       cmp preForth.$(EXT) preForth1.$(EXT)
+
+# ------------------------------------------------------------------------
+# C version
+# ------------------------------------------------------------------------
+# preForth connected to stdin - output to preForth.c
+preForth.c: preForth.pre preForth-C-backend.pre load-C-preForth.fs
+       cat preForth-C-rts.pre preForth-rts.pre preForth-C-backend.pre preForth.pre \
+          | $(HOSTFORTH) load-C-preForth.fs >preForth.c
+
+%.c: %.pre preForth preForth-C-rts.pre preForth-rts.pre
+       cat preForth-C-rts.pre preForth-rts.pre $< | ./preForth >$@
+
+%.stdC: %.c
+       gcc -O3 -Wno-implicit-function-declaration -o $@ $<
+
+# get C version assembly listing
+preForth.s: preForth.c
+       gcc -S -O3 -Wno-implicit-function-declaration -o preForth.s preForth.c
+
+
+# ------------------------------------------------------------------------
+# Docker support (for Linux version)
+# ------------------------------------------------------------------------
+# create a linux image based on Dockerfile
+.PHONY=docker-image
+docker-image: Dockerfile
+       docker build -t preforth .
+
+# run the docker image
+.PHONY=run
+run: docker-image
+       docker run -i -t --rm preforth  
+# ------------------------------------------------------------------------
+
+# ------------------------------------------------------------------------
+# simpleForth
+# ------------------------------------------------------------------------
+simpleForth.$(EXT): simpleForth.pre simpleForth-$(PLATFORM)-backend.pre preForth-$(PLATFORM)-rts.pre preForth-rts.pre preForth
+       cat preForth-$(PLATFORM)-rts.pre preForth-rts.pre simpleForth-$(PLATFORM)-backend.pre simpleForth.pre \
+            | ./preForth >simpleForth.$(EXT)
+
+simpleForth: simpleForth.$(UNIXFLAVOUR)
+       cp simpleForth.$(UNIXFLAVOUR) simpleForth
+
+%.asm: %.simple simpleForth simpleForth-$(PLATFORM)-rts.simple simpleForth-rts.simple
+       cat simpleForth-$(PLATFORM)-rts.simple simpleForth-rts.simple $< | ./simpleForth >$@
+
+
+.PHONY=clean
+clean:
+       rm -f *.asm *.o *.fas *.s *.c *.Darwin *.Linux preForthdemo simpleForthDemo simpleForth preForth forth
diff --git a/preForth/borrow.fs b/preForth/borrow.fs
new file mode 100644 (file)
index 0000000..b0d84e2
--- /dev/null
@@ -0,0 +1,56 @@
+\ Minimal Forth Workbench: main file                                        uh 2015-10-05
+
+: tick (  <spaces>name<spaces> -- comp-xt exec-xt flag )
+    STATE @ >R
+    ] >IN @ >R  BL WORD FIND
+    IF R> >IN !
+       POSTPONE [  BL WORD FIND
+    ELSE R> DROP
+       DROP 0 0 false
+    THEN
+    R> IF ] ELSE POSTPONE [ THEN ;
+
+: immediate-alias ( comp-xt exec-xt <spaces>name<spaces> -- )
+    CREATE , , IMMEDIATE DOES> STATE @ IF CELL+ THEN  @ EXECUTE ;
+
+: non-immediate-alias ( comp-xt exec-xt <spaces>name<spaces> -- )
+    CREATE , , IMMEDIATE DOES> STATE @ IF CELL+ @ COMPILE,  ELSE  @ EXECUTE THEN ;
+
+VARIABLE #primitives  0 #primitives !
+VARIABLE #words 0 #words !
+
+: another-primitive ( -- )  1 #primitives +!  1 #words +! ;
+
+: borrow ( <space>ccc<space> -- )
+    get-order
+    >IN @ >R  tick R> >IN ! NIP NIP
+    0= IF
+       forth-wordlist 1 set-order
+       another-primitive
+       >IN @ >R tick  R> >IN !  DUP 0= Abort" ?"
+       0< IF non-immediate-alias ELSE immediate-alias THEN
+      ELSE
+        CR BL WORD COUNT TYPE ."  is already defined."
+      THEN
+    set-order ;
+
+: primitive ( <space>ccc<space> -- ) borrow ;
+
+\ : later ( <space>ccc<space> -- ) \ word ccc uses late binding
+\    \ has danger of infinite recursion if no defintion exists
+\    >IN @ >R CREATE R> >IN !  
+\    HERE  BL WORD COUNT >R  
+\    HERE CHAR+ R@ MOVE  R@ CHAR+ ALLOT R> SWAP C! 
+\    DOES> COUNT EVALUATE ; 
+
+: later ( <space>ccc<space> -- ) \ word ccc uses late binding
+   >IN @ >R CREATE R> >IN !  
+   HERE  BL WORD COUNT >R  
+   HERE CHAR+ R@ MOVE  R@ CHAR+ ALLOT R> SWAP C! 
+   DOES> DUP >R
+     FIND 0= ABORT" ?" 
+     DUP >BODY R@ = IF R> COUNT TYPE ."  is not yet defined." ABORT THEN 
+     R> DROP EXECUTE ; 
+
+
+
diff --git a/preForth/forth.simple b/preForth/forth.simple
new file mode 100644 (file)
index 0000000..d8f3462
--- /dev/null
@@ -0,0 +1,487 @@
+\ simpleForth test program
+
+\ The simpleForth runtimesystem has only the words
+\
+\  bye emit key dup swap drop 0< ?exit >r r> - unnest lit
+\  branch ?branch @ c@ ! c!
+
+: over ( x1 x2 -- x1 x2 x1 )
+   >r dup r> swap ;
+
+: < ( n1 n2 -- flag )
+   - 0< ;
+
+: 1+ ( n1 -- n2 )
+   1 + ;
+
+: pick ( xn-1 ... x0 i -- xn-1 ... x0 xi )
+    over swap ?dup 0= ?exit nip swap >r 1- pick r> swap ;  
+
+: 0= ( x -- flag )
+   0 swap ?exit drop -1 ;
+
+: = ( x1 x2 -- f )
+   - 0= ;
+
+: nip ( x1 x2 -- x2 )
+   swap drop ;
+
+: 1- ( n1 -- n2 )
+   1 - ;
+
+: > ( n1 n2 -- flag )
+   swap < ;
+
+: negate ( n1 -- n2 )
+    0 swap - ;
+
+
+
+
+\ number output
+\ -------------
+
+|: (/mod  ( n d q0 -- r d q )
+   >r 2dup <  r> swap ?exit
+   >r swap over -  swap  r> 1+  (/mod ;
+
+|: 10* ( x1 -- x2 )
+    dup + dup dup + dup + + ;
+
+|: (10u/mod ( n q d -- r q d )
+   2 pick over > 0= ?exit     \ ( n q d )
+   dup >r 10*                 \ ( n q 10*d ) ( R: d )
+   (10u/mod                   \ ( r q d )
+   swap >r   0 (/mod nip   r> 10* + r> ;
+     
+|: 10u/mod ( n -- r q )
+   0 1 (10u/mod drop  ;
+
+|: (u. ( u1 -- )
+   ?dup 0= ?exit 10u/mod (u. '0' + emit ;
+
+\ display unsigned number
+: u. ( u -- )
+   dup (u. ?exit '0' emit ;
+
+
+|: (. ( n -- n' )
+   dup 0< 0= ?exit '-' emit negate ;
+
+\ display signed number
+: . ( n -- )
+   (. u. ;
+
+
+
+: cr ( -- )
+   10 emit ;
+
+32 constant bl
+
+: space ( -- )
+   bl emit ;
+
+: + ( n1 n2 -- n3 )
+    0 swap - - ;
+
+: ?dup ( x -- x x | 0 )
+   dup IF dup THEN ;
+
+: on ( addr -- )
+    -1 swap ! ;
+
+: off ( addr -- )
+     0 swap ! ;
+
+: rot ( x y z -- y z x )
+    >r swap r> swap ;
+
+: 2drop ( x1 x2 -- )
+    drop drop ;
+
+: 2dup ( x1 x2 -- x1 x2 )
+    over over ;
+
+: compare ( c-addr1 u1 c-addr2 u2 -- n )
+    rot 
+    BEGIN \ ( c-addr1 c-addr2 u1 u2 )
+      over 
+    WHILE
+      dup
+    WHILE
+      >r >r  over c@ over c@ - ?dup IF 0< dup + 1 + nip nip r> drop r> drop unnest THEN
+      1+ swap 1+ swap
+      r> 1- r> 1-
+    REPEAT
+      -1
+    ELSE
+      dup 0= IF 0 ELSE 1 THEN
+    THEN >r 2drop 2drop r> ;
+
+\ prefix? tests if c-addr1 u1 is a prefix of c-addr2 u2
+: prefix? ( c-addr1 u1 c-addr2 u2 -- f )
+    rot
+    2dup < IF 2drop 2drop 0 exit THEN
+    nip
+    BEGIN \ ( c-addr1 c-addr2 u2 )
+      ?dup
+    WHILE
+      >r  over c@ over c@ - IF 2drop r> drop 0 exit THEN
+      1+ swap 1+ swap
+      r> 1- 
+    REPEAT
+    2drop -1 ; 
+
+: .s ( i*x -- i*x )
+    depth 0= ?exit  >r .s r> dup . space ;
+
+\ TODO prefix handling
+
+: find-name ( c-addr u link -- header )
+    BEGIN
+      dup
+    WHILE 
+      >r  2dup  r> dup >r
+      l>name  dup cell+ swap @  compare  0= IF 2drop r> exit THEN
+      r> @
+    REPEAT
+    nip nip ;
+
+: cells ( n -- m )
+    dup + dup +  ;
+
+: cell+ ( addr1 -- addr2 )
+    1 cells + ;
+
+: count ( addr1 -- addr2 u )
+    dup 1+ swap c@ ;
+
+: type ( c-addr u -- )
+    BEGIN ?dup WHILE >r  count emit  r> 1- REPEAT drop ;
+
+: l>flags ( link -- flags )
+    cell+ ;
+
+: l>name ( link -- name )
+    2 cells + ;
+
+: l>interp ( link -- xt )
+    l>name  dup cell+ swap @ + ;
+
+: .name ( addr -- )
+    dup cell+ swap @ type ;
+
+: words ( -- )
+  last @
+  BEGIN
+     ?dup
+  WHILE
+     dup l>name .name space
+     @
+  REPEAT ;
+
+: min ( n1 n2 -- n3 )
+   2dup > IF swap THEN drop ;
+
+: accept ( c-addr +n1 -- +n2 )
+   dup 0= IF nip exit THEN
+   swap >r 0
+   BEGIN \ ( +n1 +n3 ) ( R: c-addr ) 
+     key dup 10 -
+   WHILE
+     over r> dup >r  + c!
+     1+ over 1- min
+   REPEAT
+   drop nip r> drop ;
+
+create tib ( -- addr )
+   80 allot 
+
+variable #tib
+
+: query ( -- )
+   tib 80 accept #tib ! ;   
+
+variable >in ( -- addr )
+
+: /string ( c-addr1 u1 n -- c-addr2 u2 )
+   swap over - >r + r> ;
+
+: source ( -- c-addr u )
+   tib   #tib @ ;
+
+: skip ( c-addr1 u1 c -- c-addr2 u2 )
+   BEGIN
+     over
+   WHILE
+     >r over c@ r> swap over =
+   WHILE
+     >r  1 /string  r> 
+   REPEAT THEN drop ;
+
+: scan ( c-addr u1 c -- c-addr2 u2 )
+   BEGIN
+     over
+   WHILE
+     >r over c@ r> swap over -
+   WHILE
+     >r  1 /string  r>
+   REPEAT THEN drop ;
+
+|: digit? ( c -- f )
+    dup '0' < IF drop 0 exit THEN '9' > 0= ;
+
+: ?# ( c-addr u -- x 0 0 | c-addr u )
+    dup 0= ?exit
+    2dup 0 >r
+    BEGIN
+      dup
+    WHILE
+      over c@ dup digit? 0= IF drop r> drop 2drop exit THEN
+      '0' - r> 10*  + >r
+      1 /string
+    REPEAT
+    2drop 2drop r> 0 0 ;
+
+: ,# ( c-addr u -- 0 0 | c-addr u )
+    dup 0= ?exit
+    ?# dup ?exit
+    ['] lit compile, rot , ;
+
+:  ?'x' ( c-addr u -- x 0 0 | c-addr u )
+    dup 0= ?exit
+    dup 3 =
+    IF over c@     ''' - ?exit
+       over 2 + c@ ''' - ?exit
+       drop 1+ c@ 0 0 THEN ;
+
+: ,'x' ( c-addr u -- 0 0 | c-addr u )
+    dup 0= ?exit
+    ?'x' dup ?exit
+    ['] lit compile, rot , ;
+    
+
+
+variable handlers
+
+variable compilers
+
+variable interpreters
+
+
+: [ ( -- )
+    interpreters @ handlers ! ;
+
+: ] ( -- )
+    compilers @ handlers ! ;
+
+1 constant #immediate
+
+code and ( x1 x2 -- x3 )
+   pop eax
+   pop edx
+   and eax, edx
+   push eax
+   next
+;
+
+code or ( x1 x2 -- x3 )
+   pop eax
+   pop edx
+   or eax, edx
+   push eax
+   next
+;
+
+: @flags ( -- x )
+    last @ l>flags @ ;
+
+: !flags ( x -- )
+    last @ l>flags ! ;
+
+: immediate ( x -- )
+    @flags #immediate or !flags ;
+
+: ,word ( c-addr1 u1 | i*x c-addr2 u2 )
+   dup 0= ?exit
+   2dup last @ find-name ?dup
+   IF nip nip dup l>flags @ #immediate and
+      IF l>interp execute ELSE l>interp compile, THEN 0 0 THEN
+;
+
+
+
+: (compilers ( c-addr u1 | i*x c-addr2 u2 )
+    ,word
+    ,#
+    ,'x'
+    over IF space type '?' emit   tail restart THEN 
+;
+
+: ?word ( c-addr1 u1 | i*x c-addr2 u2 )
+   dup 0= ?exit
+   2dup last @ find-name ?dup IF nip nip l>interp execute 0 0 THEN
+;
+
+: (interpreters ( c-addr1 u1 | i*x c-addr2 u2 )
+   ?word
+   ?#
+   ?'x'
+   over IF space type '?' emit   tail restart THEN 
+;
+
+: parse ( c -- c-addr u )
+   >r source >in @ /string
+   2dup r> dup >r  scan
+   2dup r> skip  nip source nip swap - >in !
+   nip - ;
+
+immediate: ( ( -- )
+   ')' parse 2drop ;
+
+immediate: .( ( --  )
+   ')' parse type ;
+
+immediate: \ ( -- )
+   source >in ! drop ;
+
+: parse-name ( -- c-addr u )
+   source >in @ /string
+   bl skip  2dup bl scan  source nip over - >in !    nip - ;
+
+: interpret ( -- )
+   0 0 BEGIN handlers @ execute 2drop  parse-name dup 0= UNTIL 2drop ;
+
+|: prompt ( -- )
+    cr .s
+    handlers @ compilers @ = IF ']' ELSE '>' THEN emit space ;
+
+|: .ok ( -- )
+    space 'o' emit 'k' emit ;
+
+: clearstack ( -- )
+   BEGIN depth 0< WHILE 0 REPEAT
+   BEGIN depth WHILE drop REPEAT ;
+
+\ : t{ ;
+\ : --> ;
+\ : t} ;
+
+: +! ( n addr -- )
+   dup >r @ + r> ! ;
+
+variable dp
+
+: here ( -- addr )
+   dp @ ;
+
+: allot ( n -- )
+   dp +! ;
+
+: , ( x -- )
+   here  1 cells allot  ! ;
+
+: compile, ( xt -- )
+   , ;
+
+: cmove ( c-addr1 c-addr2 u -- )
+   BEGIN
+     ?dup
+   WHILE
+     >r
+     over c@ over c!
+     1+ swap 1+ swap
+     r> 1-
+   REPEAT
+   2drop ;
+
+: header ( c-addr u -- )
+   here last @ , last !
+   0 ,   \ flags
+   dup , \ len
+   here swap dup allot
+   cmove ;
+
+: variable ( <name> -- )
+   parse-name header ['] dp @ , 0 , ;
+
+: constant ( <name> -- )
+   parse-name header ['] bl @ , , ;
+
+: : ( <name> -- )
+   parse-name header ['] ; @ , ] ;
+
+immediate: ; ( -- )
+   ['] unnest compile, [ ;
+
+immediate: IF ( -- addr )
+   ['] ?branch compile, here 0 , ;
+
+immediate: ELSE ( addr1 -- addr2 )
+   ['] branch compile, here 0 , here rot ! ;
+
+immediate: THEN ( addr -- )
+   here swap ! ;
+
+immediate: BEGIN ( -- addr )
+   here ;
+
+immediate: UNTIL ( addr -- )
+   ['] ?branch compile, , ;
+
+immediate: AGAIN ( addr -- )
+   ['] branch compile, , ;
+
+immediate: WHILE ( addr1 -- addr2 addr1 )
+   ['] ?branch compile, here 0 , swap ;
+
+immediate: REPEAT ( addr1 addr2 -- )
+   ['] branch compile, ,  here swap ! ;
+
+: restart ( -- )
+   BEGIN
+     prompt query  0 >in !  interpret  .ok
+   0 UNTIL ;
+
+: quit ( -- )
+   [ clearstack restart ;
+
+create banner ( -- addr )
+  5 c,  'F' c, 'o' c, 'r' c, 't' c, 'h' c,
+
+1 constant major ( -- x )
+2 constant minor ( -- x )
+0 constant patch ( -- x )
+
+|: .version ( -- )
+    major '0' + emit '.' emit
+    minor '0' + emit '.' emit
+    patch '0' + emit ;
+
+|: .banner ( -- )
+    cr banner count type space .version cr ;
+
+: empty ( -- )
+  last cell+ dp !  last 20 - last ! ; \ reset dictionary
+
+: cold ( -- )
+  empty warm ;
+
+: warm ( -- )
+  .banner
+  ['] (interpreters interpreters !
+  ['] (compilers compilers !
+  cr words cr
+  quit
+;
+
+
+code * ( n1 n2 -- n3 )
+   pop eax
+   pop edx
+   mul edx
+   push eax
+   next
+;
+
diff --git a/preForth/load-C-preForth.fs b/preForth/load-C-preForth.fs
new file mode 100644 (file)
index 0000000..f88f654
--- /dev/null
@@ -0,0 +1,11 @@
+\ load C preForth on top of a host Forth system
+
+include load-preForth.fs
+include preForth-C-rts.pre
+include preForth-rts.pre
+include preForth-C-backend.pre
+include preForth.pre
+
+cold
+
+bye
diff --git a/preForth/load-i386-preForth.fs b/preForth/load-i386-preForth.fs
new file mode 100644 (file)
index 0000000..7da3f75
--- /dev/null
@@ -0,0 +1,11 @@
+\ load i386 preForth on top of a host Forth system
+
+include load-preForth.fs
+include preForth-i386-rts.pre
+include preForth-rts.pre
+include preForth-i386-backend.pre
+include preForth.pre
+
+cold
+
+bye
diff --git a/preForth/load-preForth.fs b/preForth/load-preForth.fs
new file mode 100644 (file)
index 0000000..13dd2c9
--- /dev/null
@@ -0,0 +1,115 @@
+\ Load preForth on GForth or SwiftForth connected to stdin and stdout.
+
+
+defined warnings [IF] \ e.g. gforth
+ warnings off
+[THEN]
+
+defined warning [IF] \ e.g. SwiftForth
+ warning off
+[THEN]
+
+Variable ch
+
+\ key reads from stdin so it can be used with pipes and input redirection.
+: key ( -- c )
+    ch 1 stdin read-file throw
+    1 < IF  4 ( eof )  ELSE ch c@ THEN
+    ; \ dup emit ;
+
+\ This : allows for recursion by using a word's name.
+defined -smudge [IF] \ SwiftForth
+: : : -smudge ;
+[THEN]
+
+defined reveal [IF] \ gforth
+: : : reveal ;
+[THEN]
+
+
+\ Define pre and code so they skip their body
+
+: pre ( -- )
+   BEGIN refill WHILE
+     source  s" ;" compare 0= IF POSTPONE \ EXIT THEN
+   REPEAT ;
+
+: prefix pre ;
+: prelude pre ;
+: preamble pre ;
+: code pre ;
+
+: tail ;
+
+include borrow.fs
+
+wordlist Constant preForth
+
+preForth set-current
+
+: borrow borrow ;
+: primitive borrow ;
+: tail tail ;
+
+preForth 1 set-order
+
+borrow include
+borrow :
+borrow ;
+borrow \
+borrow ( 
+borrow .s
+
+borrow pre
+borrow prefix
+borrow prelude
+borrow preamble
+borrow code
+
+borrow later
+later ?dup
+later 0=
+later negate 
+later +
+later 1+
+later 1-
+later =
+later <
+later >
+later case?
+
+later over
+later rot
+later nip
+later 2drop
+later pick
+later roll
+
+later bl
+later space
+later tab
+later cr
+later u.
+later .
+
+later show
+later _dup
+later _drop
+later _swap
+
+primitive emit
+primitive key
+primitive dup
+primitive swap
+primitive 0<
+primitive ?exit
+primitive drop
+primitive recurse
+primitive >r
+primitive r>
+primitive -
+\ nest
+\ unnest
+\ lit
+
+borrow bye 
diff --git a/preForth/load-symbolic-preForth.fs b/preForth/load-symbolic-preForth.fs
new file mode 100644 (file)
index 0000000..7358501
--- /dev/null
@@ -0,0 +1,11 @@
+\ load symbolic preForth on top of a host Forth system
+
+include load-preForth.fs
+include preForth-symbolic-rts.pre
+include preForth-rts.pre
+include preForth-symbolic-backend.pre
+include preForth.pre
+
+cold
+
+bye
diff --git a/preForth/preForth-C-backend.pre b/preForth/preForth-C-backend.pre
new file mode 100644 (file)
index 0000000..22a2209
--- /dev/null
@@ -0,0 +1,181 @@
+\ preForth C backend
+
+\ alter substitutes non-letter characters by upper case letters (to aid assemblers to deal with labels).
+: replace ( c -- c d )
+    'A' swap ''' case? ?exit nip
+    'B' swap '\' case? ?exit nip
+    'C' swap ':' case? ?exit nip
+    'D' swap '.' case? ?exit nip
+    'E' swap '=' case? ?exit nip
+\   'F' swap '@' case? ?exit nip
+    'G' swap '>' case? ?exit nip
+    'H' swap ']' case? ?exit nip
+    'I' swap '1' case? ?exit nip
+    'J' swap '2' case? ?exit nip
+    'K' swap '/' case? ?exit nip
+    'L' swap '<' case? ?exit nip
+    'M' swap '-' case? ?exit nip
+    'N' swap '#' case? ?exit nip
+    'O' swap '0' case? ?exit nip
+    'P' swap '+' case? ?exit nip
+    'Q' swap '?' case? ?exit nip
+    'R' swap '"' case? ?exit nip
+    'S' swap ';' case? ?exit nip
+    'T' swap '*' case? ?exit nip   
+    'U' swap '(' case? ?exit nip
+    'V' swap '|' case? ?exit nip
+    \ also 'X' for machine code
+    'W' swap ',' case? ?exit nip
+    'Y' swap ')' case? ?exit nip
+    'Z' swap '!' case? ?exit nip
+;
+
+\ alter substitutes all non-letter characters by upper case letters.
+: alter ( c1 ... cn n -- d1 ... dn n )
+    dup 0= ?exit
+    swap >r 1- alter  r> replace  swap 1+ ;
+
+\ ------------
+\ output words
+\ ------------
+\ Output is done by emit.
+\ We define convenience words of the form ."xxx" to output xxx and >"xxx" to output xxx on a new line indented by a tab.
+
+: ."int" ( -- )
+    'i' emit 'n' emit 't' emit ;
+
+: ."return" ( -- )
+    'r' emit  'e' emit  't' emit  'u' emit  'r' emit  'n' emit ;
+
+: ."#define" ( -- )
+    '#' emit  'd' emit  'e' emit  'f' emit  'i' emit  'n' emit  'e' emit ;  
+
+: ."lit" ( -- )
+    'l' emit 'i' emit 't' emit ;
+
+: >\word ( c1 ... c2 n -- )
+   cr '/' emit '*' emit space show  space '*' emit '/' emit ;
+
+\ ------------
+\ Compiling words
+\ ------------
+
+\ reproduce a verbatim line
+: ,line ( x1 ...cn n -- )
+    show ;
+
+\ indent a verbatim line
+: ,>line ( c1 ... cn b -- )
+    cr tab ,line ;
+
+\ compile a reference to an invoked word 
+: ,word ( c1 ... cn n -- )
+   tab alter show '(' emit  ')' emit ';' emit ;
+
+\ compile a reference to an invoked word on a new line
+: ,>word ( c1 ... cn n -- )
+    cr ,word ;
+
+\ compile reference to nest primitive
+: ,nest ( -- )
+    space '{' emit ;
+
+\ compile reference to unnest primitive
+: ,unnest ( -- )
+    cr tab ."return" space 0 . ';' emit
+    cr '}' emit  cr cr ;
+
+\ compile signed number
+: ,n ( n -- )
+    . ; 
+
+\ compile unsigned number
+: ,u ( u -- )
+    u. ;
+
+\ compile literal
+: ,_lit ( c1 ... cn n -- )
+    cr tab ."lit" space  ,word ';' emit ;
+
+: ,lit ( x -- )
+    cr tab ."lit" '(' emit ')' emit space ,n ';' emit ;
+
+\ output string as comment
+: ,comment ( c1 ... cn n -- )
+    cr '/' emit '*' emit  space show  space '*' emit '/' emit ;
+
+\ create a new symbolic label
+: label ( c1 ... cn n -- )
+    cr ."int" space alter show '(' emit ')' emit ;
+
+\ body calculates the name of the body from a token
+: body ( c1 ... cn n -- c1 ... cm m )
+   'X' swap 1+ ;
+
+: ,code ( c1 ... nn n -- )
+   cr ."#define" space alter show '(' emit ')' emit ;
+
+: ,end-code ( -- )
+  cr ;
+
+\ -----------------------------------------
+
+\ tail calls
+\ C compilei is assumed to optimize tail calls
+\ so no optimization is done here.
+
+: bodylabel ( c1 ... cn n --  )
+   _drop ;
+
+\ ,tail compiles an unoptimized call     
+: ,tail  ( c1 ... cn n -- )
+   ,>word ; 
+
+\ --------------
+\ Create headers
+\ --------------
+\ preForth can optionally also create word headers with a very simple layout.
+\ 
+\ Word creation is split into parts
+\    - header     creates a dictionary header from a string on the stack.
+\    - label      creates the assembler label (with symbol substitution)
+\    - body       defined later by code (assembly code) or : (threaded code)
+\
+\ Headers are linked in a single linked list ending in 0.
+\ The link-label name of the latest definition is always as a string on top of stack.
+\ Creating a new header puts this in the link field of the new definition and
+\ replaces the link-label with the current one.
+
+
+\ link to previous header c. d is new header.
+: ,link ( c1 ... cn n d1 ... dm m -- d1 ... dm _ m+1 d1 ... dm m )
+    '_' swap 1+ _dup label  \  d_:
+    _swap  ,word  _dup
+    1- nip ;
+
+\ create a new header with given name c and flags
+\ :  header ( d1 ... dm m  c1 ... c2 n flags -- d1 ... dm m c1 ... cn n )
+\     >r
+\     ,link        \ link
+\     r>  ,u       \ flags
+\     dup ,u       \ len
+\     _dup ,string \ name
+\ ;
+
+\ dummy definition to not create a new header with given name c and flags
+:  header ( d1 ... dm m  c1 ... c2 n flags -- d1 ... dm m c1 ... cn n )
+    drop ;
+
+: ."done" ( -- )
+    'd' emit 'o' emit 'n' emit 'e' emit ; 
+
+: ."last:" ( -- )
+    'l' emit 'a' emit 's' emit 't' emit ':' emit ; 
+
+: ,end ( c1 ... cn n -- )
+    cr '/' emit '*' emit  space  ."last:" space alter show  space '*' emit '/' emit
+    cr '/' emit '*' emit  space  ."done" space '*' emit '/' emit 
+    cr ;
+
+
+\ ==== End of platform dependent part - all below should be platform independent ===
diff --git a/preForth/preForth-C-rts.pre b/preForth/preForth-C-rts.pre
new file mode 100644 (file)
index 0000000..893ce94
--- /dev/null
@@ -0,0 +1,61 @@
+\ preForth runtime system - C dependent part
+
+preamble
+/* This is a preForth generated file using preForth-C-backend. */
+/* Only modify it, if you know what you are doing. */
+
+  #include <stdio.h>
+
+  int s[10000];    /* stack */
+  int *sp=s;       /* stack pointer */
+  int r[10000];    /* return stack */
+  int *rp=r;       /* return stack pointer */
+
+#define cold main
+
+;
+
+code emit ( c -- )
+   do { putchar(*sp--); fflush(stdout); } while(0)
+;
+
+code key ( -- c )
+   do { *++sp=getchar(); if (*sp==EOF) *sp=4; } while(0)
+;
+
+code dup ( x -- x x )
+   do { int tos=*sp; *++sp=tos; } while (0)
+;
+
+code swap ( x y -- y x )
+   do { int tos=*sp; *sp=sp[-1]; sp[-1]=tos; } while(0)
+;
+
+code drop ( x -- )
+   sp--
+;
+
+code 0< ( x -- flag )
+   *sp=*sp<0?-1:0
+;
+
+code ?exit ( f -- )
+   if (*sp--) return 0
+;
+
+code >r ( x -- ) ( R -- x )
+   *++rp=*sp--
+;
+
+code r> ( R x -- ) ( -- x )
+   *++sp=*rp--
+;
+
+code - ( x1 x2 -- x3 )
+   do { int tos=*sp--; *sp-=tos; } while(0)
+;
+
+code lit ( -- )
+   *++sp=
+;
+
diff --git a/preForth/preForth-i386-backend.pre b/preForth/preForth-i386-backend.pre
new file mode 100644 (file)
index 0000000..f218fbb
--- /dev/null
@@ -0,0 +1,166 @@
+\ --------------------------
+\ preForth backend for i386 (32 bit) FASM
+\ --------------------------
+
+\ alter substitutes non-letter characters by upper case letters (to aid assemblers to deal with labels).
+: replace ( c -- c d )
+    'A' swap  39 case? ?exit nip
+    'B' swap '\' case? ?exit nip
+    'C' swap ':' case? ?exit nip
+    'D' swap '.' case? ?exit nip
+    'E' swap '=' case? ?exit nip
+    'F' swap '[' case? ?exit nip
+    'G' swap '>' case? ?exit nip
+    'H' swap ']' case? ?exit nip
+    'I' swap '1' case? ?exit nip
+    'J' swap '2' case? ?exit nip
+    'K' swap '/' case? ?exit nip
+    'L' swap '<' case? ?exit nip
+    'M' swap '-' case? ?exit nip
+    'N' swap '#' case? ?exit nip
+    'O' swap '0' case? ?exit nip
+    'P' swap '+' case? ?exit nip
+    'Q' swap '?' case? ?exit nip
+    'R' swap '"' case? ?exit nip
+\   'S' swap '!' case? ?exit nip
+    'T' swap '*' case? ?exit nip   
+    'U' swap '(' case? ?exit nip
+    'V' swap '|' case? ?exit nip
+    'W' swap ',' case? ?exit nip
+    \ also 'X' for machine code
+    'Y' swap ')' case? ?exit nip
+    'Z' swap ';' case? ?exit nip
+;
+
+\ alter substitutes all non-letter characters by upper case letters.
+: alter ( S1 -- S2 )
+    '_' 1 rot ?dup 0= ?exit nip nip
+    \ dup 0= ?exit
+    swap >r 1- alter  r> replace  swap 1+ ;
+
+\ ------------
+\ output words
+\ ------------
+\ Output is done by emit.
+\ We define convenience words of the form ."xxx" to output xxx and >"xxx" to output xxx on a new line indented by a tab.
+
+: ."dd" ( -- )
+    'D' emit 'D' emit space ;
+
+: >"dd" ( -- )
+    cr tab ."dd" ;
+
+: ."db" ( -- )
+    'D' emit 'B' emit space ;
+
+: >"db" ( -- )
+    cr tab ."db" ;
+
+: >"ds" ( -- )
+    cr tab 'D' emit 'S' emit space ;
+
+: ."nest" ( -- )
+    'n' 'e' 's' 't' 4 alter show ;
+
+: ."unnest" ( -- )
+    'u' 'n' 'n' 'e' 's' 't' 6 alter show ;
+
+: ."lit" ( -- )
+    'l' 'i' 't' 3 alter show ;
+
+\ ------------
+\ Compiling words
+\ ------------
+
+\ ,string compiles the topmost string as a sequence of numeric DB values.
+: ,string ( S -- )
+    \ ."ds" show ;
+    ?dup 0= ?exit
+    dup roll >"db" u.  \ 1st char
+    1- ,string ;
+
+\ reproduce a verbatim line
+: ,line ( x1 ...cn n -- )
+    show ;
+
+\ compile a reference to an invoked word 
+: ,word ( S -- )
+   ."dd" alter show ;
+
+\ compile a reference to an invoked word on a new line
+: ,>word ( S -- )
+    >"dd" alter show ;
+
+\ compile reference to nest primitive
+: ,nest ( -- )
+    ."dd" ."nest" ;
+
+\ compile reference to unnest primitive
+: ,unnest ( -- )
+    >"dd" ."unnest"
+    cr ;
+
+\ compile signed number
+: ,n ( n -- )
+    >"dd" . ; 
+
+\ compile unsigned number
+: ,u ( u -- )
+    >"dd" u. ;
+
+\ compile literal
+: ,_lit ( S -- )
+    >"dd" ."lit"  ,>word ;
+
+\ compile literal
+: ,lit ( x -- )
+    >"dd" ."lit"  ,n ;
+
+\ output string as comment
+: ,comment ( S -- )
+    cr tab ';' emit  space show ;
+
+\ create a new symbolic label
+: label ( S -- )
+    cr alter show ':' emit tab ;
+
+\ body calculates the name of the body from a token
+: body ( S1 -- S2 )
+   'X' swap 1+ ;
+
+\ ,codefield compiles the code field of primitive
+: ,codefield ( S -- )
+   body _dup ,word label ;
+
+: ,code ( S -- )
+    _dup label
+    ,codefield ;
+
+: ,end-code ( -- )
+  cr ;
+\ -----------------------------------
+\ tail call optimization    tail word ;  ->  [ ' word >body ] literal >r ;
+
+: bodylabel ( S --  )
+   body label ;
+
+\ ,tail compiles a tail call
+: ,tail  ( S -- )
+   body ,_lit
+   '>' 'r' 2 ,>word ;
+
+: ."done" ( -- )
+    ';' emit space 'd' emit 'o' emit 'n' emit 'e' emit ;
+
+: ."last:" ( -- )
+    ';' emit space 'l' emit 'a' emit 's' emit 't' emit ':' emit space ;
+
+: ,end ( S -- )
+    cr ."last:" alter show
+    cr ."done" cr ;
+
+\ create a new header with given name S2 and flags - do nothing
+: header ( S1 S2 flags -- S3 S2 )
+   drop ;
+
diff --git a/preForth/preForth-i386-rts.pre b/preForth/preForth-i386-rts.pre
new file mode 100644 (file)
index 0000000..09f5efd
--- /dev/null
@@ -0,0 +1,175 @@
+\ preForth runtime system - i386 (32 bit) dependent part
+\ --------------------------
+\
+\  - registers:
+\      EAX, EDX  general purpose
+\      ESI  instruction pointer
+\      EBP  return stack pointer
+\      ESP  data stack pointer
+
+prelude
+;;; This is a preForth generated file using preForth-i386-backend.
+;;; Only modify it, if you know what you are doing.
+;
+
+prefix
+format ELF 
+
+section '.bss' writeable executable
+
+       DD 10000 dup (0)
+stck:  DD 16 dup(0)
+  
+       DD 10000 dup(0)
+rstck: DD 16 dup(0)
+
+
+section '.text' executable writeable
+public main 
+extrn putchar
+extrn getchar
+extrn fflush
+extrn exit
+  
+macro next  {
+       lodsd
+       jmp dword [eax]
+}
+
+
+main:  cld
+       mov esp, dword stck
+       mov ebp, dword rstck
+       mov esi, main1
+       next
+
+main1: DD _cold
+       DD _bye  
+  
+  
+_nest:  lea ebp, [ebp-4]
+        mov [ebp], esi
+        lea esi, [eax+4]
+        next
+
+_O = 0
+  
+;
+
+code bye ( -- )
+    push ebp  
+    mov ebp, esp  
+    and esp, 0xfffffff0
+    mov eax, 0
+    mov [esp], eax
+    call exit
+;
+    
+code emit ( c -- )
+    pop eax
+
+    push ebp  
+    mov  ebp, esp
+    push eax 
+    and  esp, 0xfffffff0
+
+    mov dword [esp], eax
+    call putchar
+
+    mov eax, 0
+    mov [esp], eax
+    call fflush   ; flush all output streams
+
+    mov esp, ebp  
+    pop ebp  
+    next
+;
+
+code key ( -- c )
+        push ebp  
+        mov  ebp, esp
+        and  esp, 0xfffffff0
+        
+        call getchar
+        mov esp, ebp
+        pop ebp
+        cmp eax,-1
+        jnz key1
+        mov eax,4
+key1:   push eax
+        next
+;
+
+code dup ( x -- x x )
+        pop eax
+        push eax
+        push eax
+        next
+;
+
+code swap ( x y -- y x )
+        pop edx
+        pop eax
+        push edx
+        push eax
+        next
+;
+
+code drop ( x -- )
+        pop eax
+        next
+;
+
+code 0< ( x -- flag )
+        pop eax
+        or eax, eax
+        mov eax, 0
+        jns zless1
+        dec eax
+zless1: push eax
+        next
+;
+
+code ?exit ( f -- )
+        pop eax
+        or eax, eax
+        jz qexit1
+        mov esi, [ebp]
+        lea ebp,[ebp+4]
+qexit1: next
+;
+
+code >r ( x -- ) ( R -- x )
+        pop ebx
+        lea ebp,[ebp-4]
+        mov [ebp], ebx
+        next
+;
+
+code r> ( R x -- ) ( -- x )
+        mov eax,[ebp]
+        lea ebp, [ebp+4]
+        push eax
+        next
+;
+
+code - ( x1 x2 -- x3 )
+        pop edx
+        pop eax
+        sub eax, edx
+        push eax
+        next
+;
+
+code unnest ( -- )
+        mov esi,[ebp]
+        lea ebp,[ebp+4]
+        next
+;
+
+code lit ( -- )
+        lodsd
+        push eax
+        next
+;
diff --git a/preForth/preForth-rts.pre b/preForth/preForth-rts.pre
new file mode 100644 (file)
index 0000000..a66b851
--- /dev/null
@@ -0,0 +1,151 @@
+\ preForth runtime system - machine independent part
+
+\ ------------------------------------
+\ define lots of useful standard words
+\ ------------------------------------
+
+: ?dup ( x -- x x | 0 )
+    dup dup ?exit drop ;
+    
+: 0= ( x -- flag )
+   0 swap ?exit drop -1 ;
+
+: negate ( n1 -- n2 )
+    0 swap - ;
+
+: + ( x1 x2 -- x3 )
+    negate - ;
+
+: 1+ ( n1 -- n2 )
+   1 + ;
+
+: 1- ( n1 -- n2 )
+   1 - ;
+
+: = ( x1 x2 -- flag )
+   - 0= ;
+
+: < ( n1 n2 -- flag )
+   - 0< ;
+
+: > ( n1 n2 -- flag )
+   swap < ;
+
+\ case? compares the value x to y. If they match, return true. If not keep x and return false.
+: case? ( x y -- tf | x ff )
+    over = dup 0= ?exit  2drop -1 ; 
+
+
+\ additional stack operators
+\ --------------------------
+
+: over ( x1 x2 -- x1 x2 x1 )
+   >r dup r> swap ;
+
+: rot ( x1 x2 x3 -- x2 x3 x1 )
+   >r swap r> swap ;
+
+: nip ( x1 x2 -- x2 )
+   swap drop ;
+
+: 2drop ( x1 x2 -- )
+   drop drop ;
+
+: pick ( xn-1 ... x0 i -- xn-1 ... x0 xi )
+    over swap ?dup 0= ?exit nip swap >r 1- pick r> swap ;  
+
+: roll ( xn-1 ... x0 i -- xn-1 ... xi-1 xi+1 ... x0 xi )
+    ?dup 0= ?exit swap >r 1- roll r> swap ;
+
+
+\ text output words
+\ -----------------
+
+: bl ( -- bl )
+   32 ;
+
+: space ( -- )
+   bl emit ;
+
+: tab ( -- )
+    9 emit ;
+
+: cr ( -- )
+   10 emit ;
+
+
+\ number output
+\ -------------
+
+: (/mod  ( n d q0 -- r d q )
+   >r over over <  r> swap ?exit
+   >r swap over -  swap  r> 1+  (/mod ;
+
+: 10* ( x1 -- x2 )
+    dup + dup dup + dup + + ;
+
+: (10u/mod ( n q d -- r q d )
+   2 pick over > 0= ?exit     \ ( n q d )
+   dup >r 10*                 \ ( n q 10*d ) ( R: d )
+   (10u/mod                   \ ( r q d )
+   swap >r   0 (/mod nip   r> 10* + r> ;
+     
+: 10u/mod ( n -- r q )
+   0 1 (10u/mod drop  ;
+
+: (u. ( u1 -- )
+   ?dup 0= ?exit 10u/mod (u. '0' + emit ;
+
+\ display unsigned number
+: u. ( u -- )
+   dup (u. ?exit '0' emit ;
+
+
+: (. ( n -- n' )
+   dup 0< 0= ?exit '-' emit negate ;
+
+\ display signed number
+: . ( n -- )
+   (. u. ;
+
+
+\ -----------
+\ strings
+\ -----------
+\ Strings are represented as character stack elements with a count on top
+\ They convieniently be processed using recursion.
+\ Idioms:  dup pick   gets 1st character
+\          dup        gets length
+\          x swap 1+  adds x to end of string
+\          nip 1-     removes last character
+\
+\ Useful words
+\ show   displays trings
+\ _dup   duplicates topmost string
+\ _drop  removes topmost string
+\ _swap  exchanges two topmost strings
+
+\ show displays topmost string
+: show ( S -- )
+   ?dup 0= ?exit  swap >r 1- show r> emit ;
+
+
+: (_dup ( S m n -- S S )
+    ?dup 0= ?exit  over 2 + pick rot rot  1-   tail (_dup ;
+
+\ _dup duplicated topmost string
+: _dup ( S -- S S )
+    dup dup (_dup ;
+
+\ _drop removes topmost string
+: _drop ( S -- )
+    ?dup 0= ?exit nip 1- _drop ;
+
+
+: (_swap ( S1 S2 x1 x2 -- S2 S1 x1 0 )
+    dup 0= ?exit over 3 + roll rot rot 1- (_swap ;
+
+\ _swap exchanges two topmost strings
+: _swap ( S1 S2 -- S2 S1 )
+    dup >r pick r> dup >r  over >r + r> r> rot rot 1+ (_swap 2drop ;
+
diff --git a/preForth/preForth-symbolic-backend.pre b/preForth/preForth-symbolic-backend.pre
new file mode 100644 (file)
index 0000000..0736440
--- /dev/null
@@ -0,0 +1,201 @@
+\ preForth Symbolic Backend
+
+\ ------------
+\ output words
+\ ------------
+\ Output is done by emit.
+\ We define convenience words of the form ."xxx" to output xxx and >"xxx" to output xxx on a new line indented by a tab.
+
+: ."dw" ( -- )
+    'D' emit 'W' emit space ;
+
+: >"dw" ( -- )
+    cr tab ."dw" ;
+
+: ."db" ( -- )
+    'D' emit 'B' emit space ;
+
+: >"db" ( -- )
+    cr tab ."db" ;
+
+: >"ds" ( -- )
+    cr tab 'D' emit 'S' emit space ;
+
+: ."nest" ( -- )
+    'n' emit 'e' emit 's' emit 't' emit ;
+
+: ."unnest" ( -- )
+    'u' emit 'n' emit ."nest" ;
+
+: ."lit" ( -- )
+    'l' emit 'i' emit 't' emit ;
+
+
+\ alter substitutes non-letter characters by upper case letters (to aid assemblers to deal with labels).
+: replace ( c -- c d )
+    'A' swap  39 case? ?exit nip
+    'B' swap '\' case? ?exit nip
+    'C' swap ':' case? ?exit nip
+    'D' swap '.' case? ?exit nip
+    'E' swap '=' case? ?exit nip
+\   'F' swap '@' case? ?exit nip
+    'G' swap '>' case? ?exit nip
+    'H' swap ']' case? ?exit nip
+    'I' swap '1' case? ?exit nip
+    'J' swap '2' case? ?exit nip
+    'K' swap '/' case? ?exit nip
+    'L' swap '<' case? ?exit nip
+    'M' swap '-' case? ?exit nip
+    'N' swap '#' case? ?exit nip
+    'O' swap '0' case? ?exit nip
+    'P' swap '+' case? ?exit nip
+    'Q' swap '?' case? ?exit nip
+    'R' swap '"' case? ?exit nip
+\   'S' swap '!' case? ?exit nip
+    'T' swap '*' case? ?exit nip   
+    'U' swap '(' case? ?exit nip
+    'V' swap '|' case? ?exit nip
+    'W' swap ',' case? ?exit nip
+    \ also 'X' for machine code
+    'Y' swap ')' case? ?exit nip
+    'Z' swap ';' case? ?exit nip
+;
+
+\ alter substitutes all non-letter characters by upper case letters.
+: alter ( c1 ... cn n -- d1 ... dn n )
+    dup 0= ?exit
+    swap >r 1- alter  r> replace  swap 1+ ;
+
+\ ------------
+\ Compiling words
+\ ------------
+
+\ ,string compiles the topmost string as a sequence of numeric DB values.
+: ,string ( c1 ... cn n -- )
+    \ ."ds" show ;
+    ?dup 0= ?exit
+    dup roll >"db" u.  \ 1st char
+    1- ,string ;
+
+\ reproduce a verbatim line
+: ,line ( x1 ...cn n -- )
+    show ;
+
+\ indent a verbatim line
+: ,>line ( c1 ... cn b -- )
+    cr tab ,line ;
+
+\ compile a reference to an invoked word 
+: ,word ( c1 ... cn n -- )
+   ."dw" alter show ;
+
+\ compile a reference to an invoked word on a new line
+: ,>word ( c1 ... cn n -- )
+    >"dw" alter show ;
+
+\ compile reference to nest primitive
+: ,nest ( -- )
+    ."dw" ."nest" ;
+
+\ compile reference to unnest primitive
+: ,unnest ( -- )
+    >"dw" ."unnest" cr ;
+
+\ compile signed number
+: ,n ( n -- )
+    >"dw" . ; 
+
+\ compile unsigned number
+: ,u ( u -- )
+    >"dw" u. ;
+
+\ compile literal
+: ,_lit ( c1 ... cn n -- )
+    >"dw" ."lit"  ,>word ;
+
+: ,lit ( x -- )
+    >"dw" ."lit"  ,n ;
+
+\ output string as comment
+: ,comment ( c1 ... cn n -- )
+    cr tab '\' emit  space show ;
+
+\ create a new symbolic label
+: label ( c1 ... cn n -- )
+    cr alter show ':' emit tab ;
+
+\ body calculates the name of the body from a token
+: body ( c1 ... cn n -- c1 ... cm m )
+   'X' swap 1+ ;
+
+\ ,codefield compiles the code field of primitive
+: ,codefield ( c1 ... cn n -- )
+   body _dup ,word label ;
+
+: ,code ( c1 ... cn n -- )
+   _dup label
+   ,codefield ;
+
+: ,end-code ( -- )
+   cr ;
+
+\ -------------------------------------------------
+\ Tail call optimization   tail word ;   ->  [ ' word >body ] literal >r ;
+: bodylabel ( c1 ... cn n -- )
+   body label ;
+
+\ ,tail compiles a tail call     
+: ,tail  ( c1 ... cn n -- )
+   body ,_lit 
+   '>' 'r' 2 ,>word ; 
+
+\ --------------
+\ Create headers
+\ --------------
+\ preForth can optionally also create word headers with a very simple layout.
+\ 
+\ Word creation is split into parts
+\    - header     creates a dictionary header from a string on the stack.
+\    - label      creates the assembler label (with symbol substitution)
+\    - body       defined later by code (assembly code) or : (threaded code)
+\
+\ Headers are linked in a single linked list ending in 0.
+\ The link-label name of the latest definition is always as a string on top of stack.
+\ Creating a new header puts this in the link field of the new definition and
+\ replaces the link-label with the current one.
+
+
+\ link to previous header c. d is new header.
+: ,link ( c1 ... cn n d1 ... dm m -- d1 ... dm _ m+1 d1 ... dm m )
+    '_' swap 1+ _dup label  \  d_:
+    _swap  ,word  _dup
+    1- nip ;
+
+\ create a new header with given name c and flags
+\ :  header ( d1 ... dm m  c1 ... c2 n flags -- d1 ... dm m c1 ... cn n )
+\     >r
+\     ,link        \ link
+\     r>  ,u       \ flags
+\     dup ,u       \ len
+\     _dup ,string \ name
+\ ;
+
+\ dummy definition to not create a new header with given name c and flags
+:  header ( d1 ... dm m  c1 ... c2 n flags -- d1 ... dm m c1 ... cn n )
+    drop ;
+
+: ."done" ( -- )
+    '\' emit space 'd' emit 'o' emit 'n' emit 'e' emit ;
+
+: ."last:" ( -- )
+    '\' emit space 'l' emit 'a' emit 's' emit 't' emit ':' emit space ;
+
+: ,end ( c1 ... cn n -- )
+    cr ."last:" alter show
+    cr ."done" cr ;
+
+
+
+
+\ ==== End of platform dependent part - all below should be platform independent ===
+
diff --git a/preForth/preForth-symbolic-rts.pre b/preForth/preForth-symbolic-rts.pre
new file mode 100644 (file)
index 0000000..1f6eb40
--- /dev/null
@@ -0,0 +1,115 @@
+\ preForth runtime system - symbolic dependent part
+
+\ --------------------------
+\ define preForth primitives
+\ --------------------------
+\ These are just pseudo assembler definitions to show the overall setup.
+\ Pseudo assember assumptions:
+\  - registers:
+\      X Y   general purpose
+\      IP    instruction pointer
+\      W     pointer to body
+\ 
+\  - stacks:
+\      data   stack accessible by push pop
+\      return stack accessible by rpush rpop
+
+pre
+\ This is a preForth generated file using the preForth-symbolic-backend.
+\ Only modify it if you know what you are doing.
+;
+
+code emit ( c -- )
+   pop X  
+   swi 0   -- software interrupt 0 to output character in X
+   next
+;
+
+code key ( -- c )
+   swi 1  -- software interrupt 1 to input a character to X
+   push X
+   next
+;
+
+code dup ( x -- x x )
+     pop X
+     push X
+     push X
+     next
+;
+
+code swap ( x y -- y x )
+     pop X
+     pop Y
+     push X
+     push Y
+     next
+;
+
+code drop ( x -- )
+   pop X
+   next
+;
+
+code 0< ( x -- flag )
+       pop X
+       and X,X
+       js less1
+       mov X,#-1
+       jmp less2
+less1: xor X,X
+less2: push X
+       next
+;
+
+code ?exit
+        pop X
+        and X,X
+        jz qexit1
+        rpop IP
+qexit1: next
+;
+
+code >r ( x -- ) ( R -- x )
+   pop X
+   rpush X
+   next
+;
+
+code r> ( R x -- ) ( -- x )
+   rpop X
+   push X
+   next
+;
+
+code - ( x1 x2 -- x3 )
+   pop X
+   pop Y
+   sub X,Y
+   push X
+   next
+;
+
+code unnest ( -- )
+    rpop IP
+    next
+;
+
+code nest ( -- )
+    rpush IP
+    mov IP, W
+    next
+;
+
+code lit ( -- )
+    mov X,(IP)
+    push X
+    next
+;
+
+\ NEXT might look like this:
+\
+\ code next ( -- )
+\    mov W, (IP+)
+\    jmp (W+)
+\ ;
diff --git a/preForth/preForth.pre b/preForth/preForth.pre
new file mode 100644 (file)
index 0000000..ee9f710
--- /dev/null
@@ -0,0 +1,328 @@
+\ preForth compiler
+\
+\ features: minimal control structures, no immediate words, strings on stack, few primitives 
+\
+\ just
+\    - Stack
+\    - Returnstack
+\    - Only ?exit and recursion as control structures
+\    - :-definitions
+\    - IO via KEY/EMIT
+\    - signed single cell decimal numbers (0-9)+
+\    - character constants via 'c'-notation
+\    - output signed single cell decimal numbers
+\
+\ and 
+\    - no immediate words, i.e.
+\    - no control structures IF ELSE THEN BEGIN WHILE REPEAT UNTIL
+\    - no defining words
+\    - no DOES>
+\    - no memory @ ! CMOVE ALLOT ,
+\    - no pictured numeric output
+\    - no input stream
+\    - no state
+\    - no base
+\    - no dictionary, no EXECUTE, not EVALUATE
+\    - no CATCH and THROW
+\    - no error handling
+
+\ Prerequisites:
+\ Just 13 primitives: emit key dup swap drop 0< ?exit >r r> - nest unnest lit
+
+
+\ ---------------------------------------------------
+\ Words required from backend:
+\
+\ primitives: 
+\    emit key dup swap drop 0< ?exit >r r> - nest unnest lit
+\
+\ compiler words:
+\   ,line ,comment ,codefield ,end
+\   ,lit ,>word ,nest ,unnest ,tail
+\
+\ header creation:
+\   header label bodylabel
+\
+\ Words defined in runtime system
+\   ?dup 0= negate + 1+ 1- = < > case? over rot nip 2drop pick roll
+\   bl space tab cr u. .
+\   show _dup _drop _swap 
+
+\ -----------
+\ parse input
+\ -----------
+\ Input has only key. Ascii End of Transmission, 4, Ctrl-D signals end of input.
+\ We scan input on a character by character basis skiping whitespace (control characters) and collecting non control characters
+\ essentially extracting tokens a word boundaries.
+
+\ This all leads to the word  token  that gets the next word from the input.
+\ As preForth word names can contain symbol characters and the assembler can most likely not handle symbols in labels,
+\ a simple subsitution is defined that replaces symbols to upper case letters. Because of this all preForth definitions themselves
+\ are assumed to have lower case letters only to avoid name clashes.
+
+\ skip reads and ignores control character and returns the next non-control character (or EOF when the input is exhausted).
+: skip ( -- c )
+    key dup bl > ?exit
+    dup 4 = ?exit \ eof
+    drop   tail skip ;
+
+\ scan reads and appends non-control characters to the given string. Returns resulting string and the delimiting character.
+: scan ( S1 -- S2 bl )
+    key dup bl > 0= ?exit swap 1+ scan ;
+
+: (line ( S1 -- S2 )
+    key swap 1+ over 10 = ?exit   tail (line ;
+
+\ line reads the rest of the current line and returns it as a string.
+: line ( -- S )
+    0 (line ;
+
+\ token gets the next whitespace separated token from the input and returns it as topmost string on the stack.
+: token ( -- S )
+    skip  1 scan drop ;
+
+
+\ -----------------
+\ Code definitions
+\ -----------------
+\ preForth uses code definitions in the form
+\
+\ code <name> ( stack comment )
+\     assembly instruction
+\     ...
+\     assembly instruction
+\ ;
+\
+\ To define primitives.
+\ The assembly instructions are copied verbatim to the output. No processing takes place.
+\ Note, that the assembly instructions start at the line after code. All characters
+\ following <name> on the same line are ignored.
+\ As preForth has no immediate words, comments must be handled by code directly.
+\ ; has to be placed on a line of its own to be recognized.
+
+\ Code definitions and also the :-compiler and interpreter use a scheme of handlers trying to process
+\ the current line (or token) as topmost string. The first handler that can process the string, performs
+\ its action (possibly leaving items beneath the string) and turns the string into an empty string.
+\
+\ Code works on lines of source code.
+
+\ handle ;
+
+\ ?: detects a single ; character 
+: ?; ( S --  0 | S )
+   dup 0= ?exit
+   dup 1 - ?exit
+   over ';' - ?exit
+   _drop 0 ;
+
+\ detect if there is a ; as single character in a line 
+: ?;< ( S -- tf | S ff )
+    dup 2 - ?exit
+    2 pick ';' - ?exit
+    over 10 - ?exit
+    _drop 0 ; 
+
+\ pre just copies the following line verbatim to output until a single ; on a line of its own is detected.
+: pre ( -- )
+    line  ?;< ?dup 0= ?exit ,line   tail pre ;
+
+\ code starts a code definition. Lines are replicated to output until a single ; on a line of its own is detected.
+: code ( <name> -- )
+    token
+    _dup ,comment
+    0 header
+    ,code  line _drop  pre  ,end-code ;
+    
+\ Colon definitions - the preForth compiler
+\ -----------------------------------------
+\ preForth uses :-definitions in the form
+\
+\ : <name> ( stack comment )
+\     word 99 'x' \ comment
+\     ...
+\     word word word ;
+\
+\ To define secondary (threaded code) words. The body of the :-definition is compiled by the appropriate
+\ compiler handlers. 
+\ Note, that as for code definitions, the body starts at the line after : <name> and comment. All characters
+\ following <name> on the same line are ignored.
+\
+\ In preForth : does not switch to compiler mode but contains a loop on its own, traditionally called ] .
+\ It uses the same handler scheme as code definitions and the interpreter.
+\ 
+\ The :-compiler works on tokens.
+
+
+\ compiler handlers
+\ =================
+
+\ handle character literals
+\ -------------------------
+
+\ ?'x' detects and compiles a character literal.
+: ?'x' ( S -- 0 | S )
+       dup 0= ?exit
+       dup 3 - ?exit
+       over   ''' - ?exit
+       3 pick ''' - ?exit
+       2 pick >r _drop r>
+       ,lit 0 ;
+
+\ handle numbers
+\ --------------
+\ preForth can compile signed and unsigned decimal numbers.
+\ Digit sequences are detected by ?# and compiled by ?lit as a lit primtive followed by the number.
+\ Note, that the compiler could in principle just copy the number token to the output.
+\ The interpreter however needs to put the numbers on the stack, so the conversion is
+\ still necessary there.
+\ preForth has no base and processed decimal numbers only.
+
+\ digit checks whether a given character is a decimal digit.
+: ?digit ( c -- x 0 | c )
+    dup '0' < ?exit  '9' over < ?exit '0' - 0 ;
+
+\ ?'-' checks if the first character of the topmost string is a '-' sign.
+: ?'-' ( S -- flag )
+    dup pick >r dup >r  _drop r> 1 > r> '-' = ?exit drop 0 ;
+
+: ((?# ( S x1 -- 0 x2 ff | ci ... cn n-i x2 tf )
+      over dup 0= ?exit drop
+      >r dup pick r> swap ?digit ?dup ?exit
+      swap 10* + >r  dup roll drop 1- r>    tail ((?# ;
+
+: (?# ( S x1 -- tf | x ff )
+      ((?# >r >r _drop r> r> dup 0= ?exit nip ;
+
+ : ?-# ( S -- ci ... cn n-i tf | x ff )
+     _dup ?'-' 0= dup ?exit drop   \ check for leading '-'
+     dup roll drop 1-              \ remove leading '-'
+     0 (?# ?dup ?exit
+     negate 0 ;
+
+: ?+-#  ( S -- tf | x ff )
+     ?-# dup 0= ?exit drop     \ try to convert negative
+     0 (?# ;                   \ try to convert positive
+
+\ ?# detects and handles a signed or unsigned decimal number and puts its value on the stack.
+: ?#  ( S -- x ff | S )
+    dup 0= ?exit
+    _dup ?+-# ?exit >r _drop r> 0 ;    
+
+\ ?lit detects and handles a signed or unsigned decimal number and compiles it.
+: ?lit  ( S -- ff | S )
+    dup 0= ?exit
+    _dup ?+-# ?exit >r _drop r> ,lit 0 ;    
+
+\ Handle comments
+\ ---------------
+
+\ ?\ detects and handles \ comments by ignoring the rest of the current input line.
+: ?\ ( S -- 0 | S )
+   dup 1 -    ?exit  \ length 
+   over '\' - ?exit  \ sole character
+   _drop line _drop 0 ; \ skip rest of line
+
+
+\ Handle tail calls
+
+: ?tail ( S -- 0 | S )
+   dup 4 -      ?exit
+   4 pick 't' - ?exit 
+   3 pick 'a' - ?exit
+   2 pick 'i' - ?exit
+   over   'l' - ?exit
+   _drop  token ,tail  0 ;
+
+\ Handle words
+\ ------------
+
+\ ?word detects and handles words by compiling them as reference.
+: ?word ( S -- 0 | S )
+     dup 0= ?exit ,>word 0 ;
+
+\ Compiler loop
+\ -------------
+
+: ] ( -- )
+    token             \ get next token
+    \ run compilers
+    ?; ?dup 0= ?exit  \ ;  leave compiler loop
+    ?\                \ comment
+    ?tail
+    ?'x'              \ character literal
+    ?lit              \ number
+    ?word             \ word
+    _drop  tail ] ;   \ ignore unhandled token and cycle
+    
+\ (:  creates label for word and compiles body.
+: (:  ( S  -- )
+    _dup label   line _drop  ,nest  bodylabel  ]   ,unnest ;
+
+\ :' is the pre: that already has the intended :-functionality. However : has to be defined as last word. See below.
+: :' ( <name> -- )
+    token
+    _dup ,comment
+    0 header
+    (: ;
+
+\ -----------
+\ Interpreter
+\ -----------
+\ As preForth has no dictionary the interpreter must detect the words to execute on its own.
+\ The preForth interpreter handles
+\    - Code definitions
+\    - :-definitions
+\    - signed and unsigned decimal numbers.
+\    - \-comments
+
+\ interpreter handlers
+\ ---------------------
+
+\ ?: detects a single : token and executes the :-compiler.
+: ?: ( S c -- 0 | S )
+   dup 0= ?exit
+   dup 1 - ?exit
+   over ':' - ?exit
+   _drop :' 0 ;
+
+\ ?code detects the code token and executes the inline assembler.
+: ?code ( S -- 0 | S )
+   dup 4 - ?exit
+   4 pick 'c' - ?exit
+   3 pick 'o' - ?exit
+   2 pick 'd' - ?exit
+   over   'e' - ?exit
+   _drop code 0 ;
+
+\ ?pre detects token starting with pre and copies lines verbatim.
+: ?pre ( S -- 0 | S )
+    dup 3 < ?exit
+    dup pick     'p' - ?exit
+    dup 1 - pick 'r' - ?exit
+    dup 2 - pick 'e' - ?exit
+    _drop pre 0 ;
+
+\ quit is the top level preForth interpreter loop. It reads tokens and handles them until
+\ an error occurs or the input is exhausted.
+: quit ( -- )
+    token   \ get next token
+    \ run interpreters
+    ?:              \ :-definition
+    ?code           \ code definitions
+    ?pre            \ pre* 
+    ?\              \ comment
+    dup ?exit drop  \ unhandled or EOF
+    tail quit ;          \ cycle
+
+\ cold initializes the dictionary link and starts the interpreter. Acknowledge end on exit.
+: cold ( -- )
+    '0' 1        \ dictionary anchor
+    quit _drop   \ eof
+    \ top of dictionary as string on stack
+    ,end ;
+
+\ : is eventually defined as preForth is now complete (assuming the primitives existed).
+\ In order to bootstrap. They have to be defined.
+: : ( <name> -- )
+   :' ;
+
diff --git a/preForth/simpleForth-i386-backend.pre b/preForth/simpleForth-i386-backend.pre
new file mode 100644 (file)
index 0000000..6bb5e24
--- /dev/null
@@ -0,0 +1,255 @@
+\ simpleForth i386 backend
+
+\ alter substitutes non-letter characters by upper case letters (to aid assemblers to deal with labels).
+: replace ( c -- c d )
+    'A' swap ''' case? ?exit nip
+    'B' swap '\' case? ?exit nip
+    'C' swap ':' case? ?exit nip
+    'D' swap '.' case? ?exit nip
+    'E' swap '=' case? ?exit nip
+    'F' swap '[' case? ?exit nip
+    'G' swap '>' case? ?exit nip
+    'H' swap ']' case? ?exit nip
+    'I' swap '1' case? ?exit nip
+    'J' swap '2' case? ?exit nip
+    'K' swap '/' case? ?exit nip
+    'L' swap '<' case? ?exit nip
+    'M' swap '-' case? ?exit nip
+    'N' swap '#' case? ?exit nip
+    'O' swap '0' case? ?exit nip
+    'P' swap '+' case? ?exit nip
+    'Q' swap '?' case? ?exit nip
+    'R' swap '"' case? ?exit nip
+    'S' swap '!' case? ?exit nip
+    'T' swap '*' case? ?exit nip   
+    'U' swap '(' case? ?exit nip
+    'V' swap '|' case? ?exit nip
+    'W' swap ',' case? ?exit nip
+    \ also 'X' for machine code
+    'Y' swap ')' case? ?exit nip
+    'Z' swap ';' case? ?exit nip
+;
+
+\ alter substitutes all non-letter characters by upper case letters.
+: alter ( S1 -- S2 )
+    '_' 1 rot ?dup 0= ?exit nip nip
+    \ dup 0= ?exit
+    swap >r 1- alter  r> replace  swap 1+ ;
+
+\ ------------
+\ output words
+\ ------------
+\ Output is done by emit.
+\ We define convenience words of the form ."xxx" to output xxx and >"xxx" to output xxx on a new line indented by a tab.
+
+: ."dd" ( -- )
+    'D' emit 'D' emit space ;
+
+: >"dd" ( -- )
+    cr tab ."dd" ;
+
+: ."db" ( -- )
+    'D' emit 'B' emit space ;
+
+: >"db" ( -- )
+    cr tab ."db" ;
+
+: ."dup" ( -- )
+    'd' emit 'u' emit 'p' emit ;
+
+: ."nest" ( -- )
+    'n' 'e' 's' 't' 4 alter show ;
+
+: ."unnest" ( -- )
+    'u' 'n' 'n' 'e' 's' 't' 6 alter show ;
+
+: ."lit" ( -- )
+    'l' 'i' 't' 3 alter show ;
+
+\ ------------
+\ Compiling words
+\ ------------
+
+: escaped ( S1 -- S2 )
+    dup 0= ?exit
+    swap >r 1- escaped  r> swap 1+ over '"' - ?exit  '"' swap 1+ ; 
+    
+\ ,string compiles the topmost string as a sequence of numeric DB values.
+: ,string ( S -- )
+    >"db"  '"' emit  escaped show  '"' emit ;
+    \ ?dup 0= ?exit
+    \ dup roll >"db" u.  \ 1st char
+    \ 1- ,string ;
+
+\ reproduce a verbatim line
+: ,line ( S -- )
+    show ;
+
+\ compile a reference to an invoked word 
+: ,word ( S -- )
+   ."dd" alter show ;
+
+\ compile a reference to an invoked word on a new line
+: ,>word ( S -- )
+    >"dd" alter show ;
+
+\ compile reference to nest primitive
+: ,nest ( -- )
+    ."dd" ."nest" ;
+
+
+\ compile reference to unnest primitive
+: ,unnest ( -- )
+    >"dd" ."unnest"
+    cr ;
+
+\ reserve space 
+: ,allot ( u -- )
+    >"db" u. space ."dup" '(' emit '0' emit ')' emit ;
+
+\ compile byte
+: ,byte ( u -- )
+   >"db" space u. ;
+
+\ compile signed number
+: ,n ( n -- )
+    >"dd" . ; 
+
+\ compile unsigned number
+: ,u ( u -- )
+    >"dd" u. ;
+
+\ compile literal
+: ,_lit ( S -- )
+    >"dd" ."lit"  ,>word ;
+
+\ compile literal
+: ,lit ( x -- )
+    >"dd" ."lit"  ,n ;
+
+\ output string as comment
+: ,comment ( S -- )
+    cr tab ';' emit  space show ;
+
+: ,label ( L -- )
+    cr show ':' emit tab ;
+
+\ create a new symbolic label
+: label ( S -- )
+    alter ,label ;
+
+\ body calculates the name of the body from a token
+: body ( S1 -- S2 )
+   'X' swap 1+ ;
+
+\ ,codefield compiles the code field of primitive
+: ,codefield ( S -- )
+   body _dup ,word label ;
+
+: ,code ( S -- )
+    _dup label
+    ,codefield ;
+
+: ,end-code ( -- )
+  cr ;
+\ -----------------------------------
+\ tail call optimization    tail word ;  ->  [ ' word >body ] literal >r ;
+
+: bodylabel ( S --  )
+   body label ;
+
+\ ,tail compiles a tail call
+: ,tail  ( S -- )
+   body ,_lit
+   '>' 'r' 2 ,>word ;
+
+\ Handle conditionals
+
+\ initialize local labels
+: (label ( S1 -- S1 S2 0 ) 
+    alter '_' swap 1+  '_' swap 1+  0 ;
+
+\ deinitialize local labels
+: label) ( S m -- )
+    drop _drop ;
+
+: +label ( L1 i -- L1 L2 i+1 )
+    >r _dup   nip r> dup >r '0' + swap r> 1+ ;
+
+: ."branch" ( -- )
+    'b' 'r' 'a' 'n' 'c' 'h' 6 alter show ;
+
+: ."?branch" ( -- )
+    '?' 'b' 'r' 'a' 'n' 'c' 'h' 7 alter show ;
+
+: ,branch ( L -- )
+    >"dd" ."branch"   >"dd" show ;
+
+: ,?branch ( L -- )
+    >"dd" ."?branch"  >"dd" show ;
+
+\ codefields
+
+: ."dovar" ( -- )
+    'd' 'o' 'v' 'a' 'r' 5 alter show ;
+
+: ."doconst" ( -- )
+    'd' 'o' 'c' 'o' 'n' 's' 't' 7 alter show ;
+
+: ,dovar ( -- )
+    ."dd" ."dovar" ;
+
+: ,doconst ( -- )
+    ."dd" ."doconst" ;
+
+
+\ prologue and epilogue
+
+: ,opening  ( -- )
+;
+
+: ."done" ( -- )
+    ';' emit space 'd' emit 'o' emit 'n' emit 'e' emit ;
+
+: ."last:" ( -- )
+    ';' emit space 'l' emit 'a' emit 's' emit 't' emit ':' emit space ;
+
+: ,ending ( S -- )
+    'l' 'a' 's' 't' 4  0 header cr tab _dup label ,dovar bodylabel _dup ."dd" alter show  
+    100000 ,allot 
+    'm' 'e' 'm' 't' 'o' 'p' 6 ,label  0 ,u
+    cr ."last:" alter show
+    cr ."done" cr ;
+
+\ --------------
+\ Create headers
+\ --------------
+\ preForth can optionally also create word headers with a very simple layout.
+\ 
+\ Word creation is split into parts
+\    - header     creates a dictionary header from a string on the stack.
+\    - label      creates the assembler label (with symbol substitution)
+\    - body       defined later by code (assembly code) or : (threaded code)
+\
+\ Headers are linked in a single linked list ending in 0.
+\ The link-label name of the latest definition is always as a string on top of stack.
+\ Creating a new header puts this in the link field of the new definition and
+\ replaces the link-label with the current one.
+
+
+\ link to previous header c. d is new header.
+: ,link ( S1 S2 -- S3 S2 )
+    '_' swap 1+ _dup label  \  d_:
+    _swap  ,word  _dup
+    1- nip ;
+
+\ create a new header with given name S2 and flags, S1 is the last link label
+: header ( S1 S2 flags -- S3 S2 )
+    >r
+    ,link        \ link
+    r>  ,u       \ flags
+    dup ,u       \ len
+    _dup ,string \ name
+;
+
diff --git a/preForth/simpleForth-i386-rts.simple b/preForth/simpleForth-i386-rts.simple
new file mode 100644 (file)
index 0000000..4ba8aff
--- /dev/null
@@ -0,0 +1,290 @@
+\ simpleForth runtimesystem - i386 (32 bit) dependent part
+\ --------------------------------------------------------
+
+\  - registers:
+\      EAX, EDX  general purpose
+\      ESI  instruction pointer
+\      EBP  return stack pointer
+\      ESP  data stack pointer
+
+prelude
+;;; This is a simpleForth generated file using simpleForth-i386-backend.
+;;; Only modify it, if you know what you are doing.
+;
+
+\ --------------------------
+\ simpleForth primitives for i386 (32 bit)
+\ --------------------------
+
+prefix
+format ELF 
+
+section '.bss' executable writable
+
+       DD 10000 dup(0)
+stck:  DD 16 dup(0)
+  
+       DD 10000 dup(0)
+rstck: DD 16 dup(0)
+
+
+section '.text' executable writable align 4096
+
+public main 
+extrn putchar
+extrn getchar
+extrn fflush
+extrn exit
+extrn mprotect
+; extrn __error  ; Mac OS
+; extrn __errno_location ; Linux
+  
+macro next  {
+       lodsd
+       jmp dword [eax]
+}
+
+origin:
+
+main:  cld
+       mov esp, dword stck
+       mov ebp, dword rstck
+
+       ; make section writable
+       push ebp
+       mov ebp, esp
+       sub esp, 16
+       and esp, 0xfffffff0
+       mov dword [esp+8], 7  ; rwx
+       mov eax, memtop
+       sub eax, origin
+       mov dword [esp+4], eax
+       mov dword [esp], origin
+       call mprotect
+       mov esp, ebp
+       pop ebp
+       or eax, eax     ; error?   
+       jz main0
+       push ebp  
+       mov ebp, esp
+       push eax
+       and esp, 0xfffffff0
+       ; call __error    ; get error code on Mac OS
+       ; mov eax, [eax]
+       ; call __errno_location ; get error on Linux
+       ; mov eax, [eax]
+       mov [esp], eax
+       call exit
+
+main0: mov esi, main1
+       next
+
+main1: DD _cold
+       DD _bye  
+  
+  
+_nest:  lea ebp, [ebp-4]
+        mov [ebp], esi
+        lea esi, [eax+4]
+        next
+
+_dovar: lea eax,[eax+4]
+       push eax
+        next
+
+_doconst:
+        push dword [eax+4]
+        next
+
+_dodefer:
+       mov eax, [eax+4]
+        jmp dword [eax]
+       
+_O = 0
+
+;
+
+code bye ( -- )
+    push ebp  
+    mov ebp, esp  
+    and esp, 0xfffffff0
+    mov eax, 0
+    mov [esp], eax
+    call exit
+;
+    
+code emit ( c -- )
+    pop eax
+
+    push ebp  
+    mov  ebp, esp
+    push eax 
+    and  esp, 0xfffffff0
+
+    mov dword [esp], eax
+    call putchar
+
+    mov eax, 0
+    mov [esp], eax
+    call fflush   ; flush all output streams
+
+    mov esp, ebp  
+    pop ebp  
+    next
+;
+
+code key ( -- c )
+        push ebp  
+        mov  ebp, esp
+        and  esp, 0xfffffff0
+        
+        call getchar
+        mov esp, ebp
+        pop ebp
+        cmp eax,-1
+        jnz key1
+        mov eax,4
+key1:   push eax
+        next
+;
+
+code dup ( x -- x x )
+        pop eax
+        push eax
+        push eax
+        next
+;
+
+code swap ( x y -- y x )
+        pop edx
+        pop eax
+        push edx
+        push eax
+        next
+;
+
+code drop ( x -- )
+        pop eax
+        next
+;
+
+code 0< ( x -- flag )
+        pop eax
+        or eax, eax
+        mov eax, 0
+        jns zless1
+        dec eax
+zless1: push eax
+        next
+;
+
+code ?exit ( f -- )
+        pop eax
+        or eax, eax
+        jz qexit1
+        mov esi, [ebp]
+        lea ebp,[ebp+4]
+qexit1: next
+;
+
+code >r ( x -- ) ( R -- x )
+        pop ebx
+        lea ebp,[ebp-4]
+        mov [ebp], ebx
+        next
+;
+
+code r> ( R x -- ) ( -- x )
+        mov eax,[ebp]
+        lea ebp, [ebp+4]
+        push eax
+        next
+;
+
+code - ( x1 x2 -- x3 )
+        pop edx
+        pop eax
+        sub eax, edx
+        push eax
+        next
+;
+
+code unnest ( -- )
+        mov esi,[ebp]
+        lea ebp,[ebp+4]
+        next
+;
+
+code exit ( -- )
+        mov esi,[ebp]
+        lea ebp,[ebp+4]
+        next
+;
+
+
+code lit ( -- )
+        lodsd
+        push eax
+        next
+;
+
+code branch ( -- )
+        lodsd
+        mov esi,eax
+        next
+;
+
+code ?branch ( f -- )
+        pop eax
+        or eax,eax
+        jz _branchX
+       lea esi,[esi+4]
+        next
+;
+
+code @ ( addr -- x )
+        pop eax
+        mov eax,[eax]
+       push eax
+        next
+;
+
+code c@ ( c-addr -- c )
+        pop edx
+       xor eax, eax
+        mov al,byte [edx]
+       push eax
+        next
+;
+
+code ! ( x addr -- )
+        pop edx
+        pop eax
+        mov dword [edx],eax
+        next    
+;
+
+code c! ( c c-addr -- )
+        pop edx
+        pop eax
+        mov byte [edx], al
+        next
+;
+
+code execute ( xt -- )
+        pop eax
+        jmp dword [eax]
+;
+
+code depth ( -- n )
+        mov eax, stck
+       sub eax, esp
+       sar eax,2
+        push eax
+       next
+;
+
+\ pre
+\     section '.data' writable executable 
+\ ;
diff --git a/preForth/simpleForth-rts.simple b/preForth/simpleForth-rts.simple
new file mode 100644 (file)
index 0000000..ea98cc2
--- /dev/null
@@ -0,0 +1,4 @@
+\ preForth runtime system - machine independent part
+
+\ empty up to now
+
diff --git a/preForth/simpleForth.pre b/preForth/simpleForth.pre
new file mode 100644 (file)
index 0000000..a2ccb37
--- /dev/null
@@ -0,0 +1,547 @@
+\ simpleForth
+
+\ ---------------------------------------------------
+\ Words required from backend:
+\
+\ primitives: 
+\    emit key dup swap drop 0< ?exit >r r> - nest unnest lit
+\    branch ?branch
+\
+\ compiler words:
+\   ,line ,comment ,codefield ,opening ,ending
+\   ,lit ,>word ,nest ,unnest ,tail
+\
+\ control structures
+\   ,branch ,?branch +label ,label (label label)
+\
+\ header creation:
+\   header label bodylabel
+\
+\ Words defined in runtime system
+\   ?dup 0= negate + 1+ 1- = < > case? over rot nip 2drop pick roll
+\   bl space tab cr u. .
+\   show _dup _drop _swap 
+
+\ -----------
+\ parse input
+\ -----------
+\ Input has only key. Ascii End of Transmission, 4, Ctrl-D signals end of input.
+\ We scan input on a character by character basis skiping whitespace (control characters) and collecting non control characters
+\ essentially extracting tokens a word boundaries.
+
+\ This all leads to the word  token  that gets the next word from the input.
+\ As preForth word names can contain symbol characters and the assembler can most likely not handle symbols in labels,
+\ a simple subsitution is defined that replaces symbols to upper case letters. Because of this all preForth definitions themselves
+\ are assumed to have lower case letters only to avoid name clashes.
+
+\ skip reads and ignores control character and returns the next non-control character (or EOF when the input is exhausted).
+: skip ( -- c )
+    key dup bl > ?exit
+    dup 4 = ?exit \ eof
+    drop   tail skip ;
+
+\ scan reads and appends non-control characters to the given string. Returns resulting string and the delimiting character.
+: scan ( S1 -- S2 bl )
+    key dup bl > 0= ?exit swap 1+ scan ;
+
+: (line ( S1 -- S2 )
+    key swap 1+ over 10 = ?exit   tail (line ;
+
+\ line reads the rest of the current line and returns it as a string.
+: line ( -- S )
+    0 (line ;
+
+\ token gets the next whitespace separated token from the input and returns it as topmost string on the stack.
+: token ( -- S )
+    skip  1 scan drop ;
+
+
+\ -----------------
+\ Code definitions
+\ -----------------
+\ preForth uses code definitions in the form
+\
+\ code <name> ( stack comment )
+\     assembly instruction
+\     ...
+\     assembly instruction
+\ ;
+\
+\ To define primitives.
+\ The assembly instructions are copied verbatim to the output. No processing takes place.
+\ Note, that the assembly instructions start at the line after code. All characters
+\ following <name> on the same line are ignored.
+\ As preForth has no immediate words, comments must be handled by code directly.
+\ ; has to be placed on a line of its own to be recognized.
+
+\ Code definitions and also the :-compiler and interpreter use a scheme of handlers trying to process
+\ the current line (or token) as topmost string. The first handler that can process the string, performs
+\ its action (possibly leaving items beneath the string) and turns the string into an empty string.
+\
+\ Code works on lines of source code.
+
+\ handle ;
+
+\ ?: detects a single ; character 
+: ?; ( S --  0 | S )
+   dup 0= ?exit
+   dup 1 - ?exit
+   over ';' - ?exit
+   _drop 0 ;
+
+\ detect if there is a ; as single character in a line 
+: ?;< ( S -- tf | S ff )
+    dup 2 - ?exit
+    2 pick ';' - ?exit
+    over 10 - ?exit
+    _drop 0 ; 
+
+\ pre just copies the following line verbatim to output until a single ; on a line of its own is detected.
+: pre ( -- )
+    line  ?;< ?dup 0= ?exit ,line   tail pre ;
+
+\ code starts a code definition. Lines are replicated to output until a single ; on a line of its own is detected.
+: code ( <name> -- )
+    token
+    _dup ,comment
+    0 header
+    ,code  line _drop  pre  ,end-code ;
+    
+\ Colon definitions - the preForth compiler
+\ -----------------------------------------
+\ preForth uses :-definitions in the form
+\
+\ : <name> ( stack comment )
+\     word 99 'x' \ comment
+\     ...
+\     word word word ;
+\
+\ To define secondary (threaded code) words. The body of the :-definition is compiled by the appropriate
+\ compiler handlers. 
+\ Note, that as for code definitions, the body starts at the line after : <name> and comment. All characters
+\ following <name> on the same line are ignored.
+\
+\ In preForth : does not switch to compiler mode but contains a loop on its own, traditionally called ] .
+\ It uses the same handler scheme as code definitions and the interpreter.
+\ 
+\ The :-compiler works on tokens.
+
+
+\ compiler handlers
+\ =================
+
+\ handle character literals
+\ -------------------------
+
+\ ?'x' detects and pushes it on the stack
+: ?'x' ( S -- x 0 | S )
+       dup 0= ?exit
+       dup 3 - ?exit
+       over   ''' - ?exit
+       3 pick ''' - ?exit
+       2 pick >r _drop r>
+       0 ;
+
+\ ?'x'lit detects and compiles a character literal.
+: ?'x'lit ( S -- 0 | S )
+       dup 0= ?exit
+       dup 3 - ?exit
+       over   ''' - ?exit
+       3 pick ''' - ?exit
+       2 pick >r _drop r>
+       ,lit 0 ;
+
+\ handle numbers
+\ --------------
+\ preForth can compile signed and unsigned decimal numbers.
+\ Digit sequences are detected by ?# and compiled by ?lit as a lit primtive followed by the number.
+\ Note, that the compiler could in principle just copy the number token to the output.
+\ The interpreter however needs to put the numbers on the stack, so the conversion is
+\ still necessary there.
+\ preForth has no base and processed decimal numbers only.
+
+\ digit checks whether a given character is a decimal digit.
+: ?digit ( c -- x 0 | c )
+    dup '0' < ?exit  '9' over < ?exit '0' - 0 ;
+
+\ ?'-' checks if the first character of the topmost string is a '-' sign.
+: ?'-' ( S -- flag )
+    dup pick >r dup >r  _drop r> 1 > r> '-' = ?exit drop 0 ;
+
+: ((?# ( S x1 -- 0 x2 ff | S2 x2 tf )
+      over dup 0= ?exit drop
+      >r dup pick r> swap ?digit ?dup ?exit
+      swap 10* + >r  dup roll drop 1- r>    tail ((?# ;
+
+: (?# ( S x1 -- tf | x ff )
+      ((?# >r >r _drop r> r> dup 0= ?exit nip ;
+
+ : ?-# ( S -- ci ... cn n-i tf | x ff )
+     _dup ?'-' 0= dup ?exit drop   \ check for leading '-'
+     dup roll drop 1-              \ remove leading '-'
+     0 (?# ?dup ?exit
+     negate 0 ;
+
+: ?+-#  ( S -- tf | x ff )
+     ?-# dup 0= ?exit drop     \ try to convert negative
+     0 (?# ;                   \ try to convert positive
+
+\ ?# detects and handles a signed or unsigned decimal number and puts its value on the stack.
+: ?#  ( S -- x ff | S )
+    dup 0= ?exit
+    _dup ?+-# ?exit >r _drop r> 0 ;    
+
+\ ?lit detects and handles a signed or unsigned decimal number and compiles it.
+: ?lit  ( S -- ff | S )
+    dup 0= ?exit
+    _dup ?+-# ?exit >r _drop r> ,lit 0 ;    
+
+\ Handle comments
+\ ---------------
+
+\ ?\ detects and handles \ comments by ignoring the rest of the current input line.
+: ?\ ( S -- 0 | S )
+   dup 1 -    ?exit  \ length 
+   over '\' - ?exit  \ sole character
+   _drop line _drop 0 ; \ skip rest of line
+
+
+\ Handle tail calls
+
+: ?tail ( S -- 0 | S )
+   dup 0= ?exit
+   dup 4 -      ?exit
+   4 pick 't' - ?exit 
+   3 pick 'a' - ?exit
+   2 pick 'i' - ?exit
+   over   'l' - ?exit
+   _drop  token ,tail  0 ;
+
+
+\ Handle control structures
+
+: ?if ( L1 n1 S -- L1 L2 n2 0 | L1 n1 S )
+   dup 0= ?exit
+   dup 2 -      ?exit
+   2 pick 'I' - ?exit
+   over   'F' - ?exit
+   _drop 
+   +label >r _dup ,?branch r> 0 ;
+
+: ?else ( L1 L2 n1 S -- L1 L3 L2 n2 0 | L1 L2 n1 S )
+   dup 0= ?exit
+   dup 4 -      ?exit
+   4 pick 'E' - ?exit
+   3 pick 'L' - ?exit
+   2 pick 'S' - ?exit
+   over   'E' - ?exit
+   _drop 
+   +label >r  _dup ,branch  _swap ,label r> 0 ;
+
+: ?then ( L1 L2 n1 S -- L1 n2 0 | L1 L2 n1 S )
+   dup 0= ?exit
+   dup 4 -      ?exit
+   4 pick 'T' - ?exit
+   3 pick 'H' - ?exit
+   2 pick 'E' - ?exit
+   over   'N' - ?exit
+   _drop
+   >r ,label r> 0 ;
+
+: ?begin ( L1 n1 S -- L1 L2 n2 0 | L1 n1 S )
+   dup 0= ?exit
+   dup 5 -      ?exit
+   5 pick 'B' - ?exit
+   4 pick 'E' - ?exit
+   3 pick 'G' - ?exit
+   2 pick 'I' - ?exit
+   over   'N' - ?exit
+   _drop
+   +label >r _dup ,label r> 0 ;
+
+: ?while ( S -- 0 | S )
+   dup 0= ?exit
+   dup 5 -      ?exit
+   5 pick 'W' - ?exit
+   4 pick 'H' - ?exit
+   3 pick 'I' - ?exit
+   2 pick 'L' - ?exit
+   over   'E' - ?exit
+   _drop
+   +label >r _dup ,?branch _swap r> 0 ;
+
+: ?repeat ( S -- 0 | S )
+   dup 0= ?exit
+   dup 6 -      ?exit
+   6 pick 'R' - ?exit
+   5 pick 'E' - ?exit
+   4 pick 'P' - ?exit
+   3 pick 'E' - ?exit
+   2 pick 'A' - ?exit
+   over   'T' - ?exit
+   _drop
+   >r ,branch ,label r> 0 ;
+
+: ?until ( S -- 0 | S )
+   dup 0= ?exit
+   dup 5 -      ?exit
+   5 pick 'U' - ?exit
+   4 pick 'N' - ?exit
+   3 pick 'T' - ?exit
+   2 pick 'I' - ?exit
+   over   'L' - ?exit
+   _drop
+   >r ,?branch r> 0 ;
+
+\ ?'x' detects and pushes it on the stack
+: ?['] ( S -- x 0 | S )
+       dup 0= ?exit
+       dup 3 - ?exit
+       3 pick '[' - ?exit
+       2 pick ''' - ?exit
+       over   ']' - ?exit
+       _drop
+       token ,_lit
+       0 ;
+
+
+
+\ Handle words
+\ ------------
+
+\ ?word detects and handles words by compiling them as reference.
+: ?word ( S -- 0 | S )
+     dup 0= ?exit ,>word 0 ;
+
+\ Compiler loop
+\ -------------
+
+: ] ( -- )
+    token             \ get next token
+    \ _dup cr tab ';' emit space show 
+    \ run compilers
+    ?; ?dup 0= ?exit  \ ;  leave compiler loop
+    ?\                \ comment
+    ?tail
+    ?if
+    ?else
+    ?then
+    ?begin
+    ?while
+    ?repeat
+    ?until
+    ?[']
+    ?'x'lit           \ character literal
+    ?lit              \ number
+    ?word             \ word
+    _drop  tail ] ;   \ ignore unhandled token and cycle
+    
+\ (:  creates label for word and compiles body.
+: (:  ( S  -- )
+    _dup label   line _drop  ,nest _dup bodylabel (label   ]  label)  ,unnest ;
+
+: |: ( <name> -- )
+    token (: ;
+
+: immediate: ( <name> -- )
+    \ cr ';' emit 'i' emit 
+    token _dup ,comment  1 header (: ;
+
+
+\ :' is the pre: that already has the intended :-functionality. However : has to be defined as last word. See below.
+: :' ( <name> -- )
+    token
+    _dup ,comment
+    0 header
+    (: ;
+
+: , ( x -- )
+    ,u ;
+
+: c, ( x -- )
+    ,byte ;
+
+: allot ( u -- )
+    ,allot ;
+
+: create ( <name> -- )
+    token _dup ,comment
+    0 header  _dup label   line _drop ,dovar    bodylabel ;
+
+: variable ( <name> -- )
+    create 0 ,u ;
+
+: constant ( <name> L x -- )
+    >r 
+    token  _dup ,comment
+    0 header  _dup label   line _drop ,doconst  bodylabel  r> ,u ;
+
+
+\ -----------
+\ Interpreter
+\ -----------
+\ As preForth has no dictionary the interpreter must detect the words to execute on its own.
+\ The preForth interpreter handles
+\    - Code definitions
+\    - :-definitions
+\    - signed and unsigned decimal numbers.
+\    - \-comments
+
+\ interpreter handlers
+\ ---------------------
+
+\ ?|: handle headerless :-definitions
+: ?|: ( S -- 0 | S )
+   dup 0= ?exit
+   dup 2 - ?exit
+   2 pick '|' - ?exit
+   over   ':' - ?exit
+   _drop
+   |: 0 ;
+
+: ?immediate: ( S -- 0 | S )
+   dup 0= ?exit
+   dup 10 - ?exit
+   10 pick 'i' - ?exit
+    9 pick 'm' - ?exit
+    8 pick 'm' - ?exit
+    7 pick 'e' - ?exit
+    6 pick 'd' - ?exit
+    5 pick 'i' - ?exit
+    4 pick 'a' - ?exit
+    3 pick 't' - ?exit
+    2 pick 'e' - ?exit
+   over    ':' - ?exit
+   _drop
+   immediate: 0 ;
+
+\ ?: detects a single : token and executes the :-compiler.
+: ?: ( S -- 0 | S )
+   dup 0= ?exit
+   dup 1 - ?exit
+   over ':' - ?exit
+   _drop :' 0 ;
+
+
+: ?, ( S -- 0 | S )
+   dup 0= ?exit
+   dup 1 - ?exit
+   over ',' - ?exit
+   _drop , 0 ;
+
+: ?c, ( S -- 0 | S )
+   dup 0= ?exit
+   dup 2 - ?exit
+   2 pick 'c' - ?exit
+   over ',' - ?exit
+   _drop c, 0 ;
+
+\ ?allot detects the variablecreate token and creates a word w/o parameter field
+: ?allot ( S -- 0 | S )
+    dup 0= ?exit
+    dup 5 - ?exit
+    5 pick 'a' - ?exit
+    4 pick 'l' - ?exit
+    3 pick 'l' - ?exit
+    2 pick 'o' - ?exit
+    over   't' - ?exit
+    _drop
+    allot 0 ;
+
+\ ?create detects the variablecreate token and creates a word w/o parameter field
+: ?create ( S -- 0 | S )
+    dup 0= ?exit
+    dup 6 - ?exit
+    6 pick 'c' - ?exit
+    5 pick 'r' - ?exit
+    4 pick 'e' - ?exit
+    3 pick 'a' - ?exit
+    2 pick 't' - ?exit
+    over   'e' - ?exit
+    _drop
+    create 0 ;
+
+\ ?variable detects the variable token and creates a variable
+: ?variable ( S -- 0 | S )
+    dup 0= ?exit
+    dup 8 - ?exit
+    8 pick 'v' - ?exit
+    7 pick 'a' - ?exit
+    6 pick 'r' - ?exit
+    5 pick 'i' - ?exit
+    4 pick 'a' - ?exit
+    3 pick 'b' - ?exit
+    2 pick 'l' - ?exit
+    over   'e' - ?exit
+    _drop
+    variable 0 ;
+
+\ ?constant detects the constant token and creates a constant
+: ?constant ( i*x <name> -- )
+    dup 0= ?exit
+    dup 8 - ?exit
+    8 pick 'c' - ?exit
+    7 pick 'o' - ?exit
+    6 pick 'n' - ?exit
+    5 pick 's' - ?exit
+    4 pick 't' - ?exit
+    3 pick 'a' - ?exit
+    2 pick 'n' - ?exit
+    over   't' - ?exit
+    _drop
+    constant 0 ;
+
+
+\ ?code detects the code token and executes the inline assembler.
+: ?code ( S -- 0 | S )
+   dup 4 - ?exit
+   4 pick 'c' - ?exit
+   3 pick 'o' - ?exit
+   2 pick 'd' - ?exit
+   over   'e' - ?exit
+   _drop code 0 ;
+
+\ ?pre detects token starting with pre and copies lines verbatim.
+: ?pre ( S -- 0 | S )
+    dup 3 < ?exit
+    dup pick     'p' - ?exit
+    dup 1 - pick 'r' - ?exit
+    dup 2 - pick 'e' - ?exit
+    _drop pre 0 ;
+
+\ quit is the top level preForth interpreter loop. It reads tokens and handles them until
+\ an error occurs or the input is exhausted.
+: quit ( -- )
+    token   \ get next token
+    \ cr ';' emit space _dup show cr
+    \ run interpreters
+    ?:              \ :-definition
+    ?|:             \ headerless definitions
+    ?immediate:     \ immediate definitions
+    ?code           \ code definitions
+    ?pre            \ pre*
+    ?create         \ create definitions
+    ?variable       \ variable definition
+    ?constant       \ constant definition
+    ?,              \ comma
+    ?c,             \ byte comma
+    ?allot          \ allot
+    ?#              \ signed decimal number
+    ?'x'            \ character literal
+    ?\              \ comment
+    dup ?exit drop  \ unhandled or EOF
+    tail quit ;     \ cycle
+
+\ cold initializes the dictionary link and starts the interpreter. Acknowledge end on exit.
+: cold ( -- )
+    ,opening
+    '0' 1        \ dictionary anchor
+    quit _drop   \ eof
+    \ top of dictionary as string on stack
+    ,ending ;
+
+\ : is eventually defined as preForth is now complete (assuming the primitives existed).
+\ In order to bootstrap. They have to be defined.
+: : ( <name> -- )
+   :' ;
+