Just a little exercise ("SQRT the hard way") rehosting the tiny FORTH
system from:
"forth in (many lines of) javascript"
http://lists.canonical.org/pipermail/kragen-discuss/2013-October/
001278.html
in dc(1)[0]. Now, dc does have a stack, but it doesn't have any way
to input or manipulate strings, so everything has to be goedel coded
[1]. I was lazy enough to use the dc arrays as the equivalent of
jump tables in assembly[2], not lazy enough (read: too stupid) to
have used them as dictionaries. Because the forth dictionary was
hand-rolled, it was exceedingly tempting to kludge the IMMediate flag
onto the normal dictionary entry.
-Dave
NB. Despite dc's arbitrary precision capability, there are many parts
of this system (especially the kludge for '<=') which are limited to
fixed, or even unknown but fairly small, values.
[0] I now have much more respect for Robert Morris and Lorinda Cherry
(authors of the original bc, which compiled to dc). The original
inspiration for this exercise was running across the 'a' command,
which would enable subroutine-threaded interpretation of native dc
code goedel-coded as integers, however, this implementation[3]
vectors through a primitive array and hence no longer uses 'a'.
[1] kludged here w/ awk(1)
[2] or sometimes just to avoid pressure in the single-character dc
namespace
[3] indirect threaded for convenience of CREATE DOES>
#!/bin/sh
########################################################################
# The FORTH application for integer SQRT. Output should be:
#
# 8 2
# 10 3
# 2013 44
########################################################################
prog=$(cat <<"EOF"
VAR P VAR Q VAR R
: SQRT R ! 0 P !
1 REPEAT DUP R @ <= IF 2* 2* AGAIN THEN Q !
REPEAT Q @ 1 <= NOT IF
Q @ 2/ 2/ Q !
P @ DUP 2/ P ! Q @ +
DUP R @ <= IF P @ Q @ + P !
DUP R @ SWAP - R !
THEN
DROP
AGAIN THEN
P @ ;
: TAB DUP . SQRT . CR ;
CR
8 TAB
10 TAB
2013 TAB
EOF
)
########################################################################
# FORTH words (control & data) implementing application language
########################################################################
boot=$(tail +2 <<"EOF"
' !!! this line is a kludge to keep the shell HEREDOC happy
' DO: , LABEL : [/] LIT DO: , LABEL [/] ' [/] ,
: ; LIT NEXT , [/] ' [/] , IMM
: REF> HERE HERE , ;
: <DEF HERE !~ ;
: IF LIT JZ , REF> ; IMM
: THEN <DEF ; IMM
: REPEAT R> HERE >R >R ; IMM
: AGAIN LIT JMP , R> R> , >R ; IMM
: CREATE LIT NEXT , LABEL ;
: DOES> LIT CFA , LIT LIT , REF> LIT !~ , LIT NEXT ,
LIT DOES: , <DEF ; IMM
: VAR 0 CREATE , DOES> ;
: ! SWAP !~ ;
EOF
)
########################################################################
# a FORTH in dc(1)
# we use the dc stack for data (exercise: replace by a variable)
# but we synthesize our own return stack and memory w/ dictionary.
# as dc doesn't do string input, nor partial-line numeric output,
# we must also hand-translate numbers (goedel-codes in, ascii out)
########################################################################
forthvm=$(cat <<"EOF"
# c=compile state, d=dictionary, h=HERE, i=IP, k=key/CFA kludge
# l=lastdef, m=MEMORY (array), p=primitive count, r=RSTACK
# x,y,z,_=scratch
## Dictionary/Memory ####################
[lh d 1+sh :m]s, # (,) append to memory
[1000~ l,x d0<S]sS # (S)tring literal
[ld l,x lhsd lSx lhsl 0* l,x]sE # (E)nter in dictionary
[lz1-sy 0 2Q]sB # (B)ad match
[ly;m800/sx ly1+sy 1 2Q]sG # (G)ood match
[lx200% ly;m200%- 0!=B lx 0=G lx1000/sx ly1+sy 0;Qx]0:Q # (Q)uery 1
[ly;msy lksx ly 0<Q]sC # (C)ontinue search
[lxsk lysz 0;Qx 0=C]sQ # (Q)uery dictionary
## I/O ##################################
[lyP9Pq]sU # o(U)tput number
[lx0=U lx 10~48+lzd1+sz256r^*ly+sy sx 1;Ux]1:U # digit
[48sy d0=U sx 0sz 0sy 1;Ux]0:U # *entry
[lyq]sN # i(N)put number
[lx0=N lx 1000~48-lzd1+sz10r^*ly+sy sx 1;Nx]1:N # digit
[sx 0sz 0sy 1;Nx]0:N # *entry
## Inner Interpreter ####################
[500-;Oxq]sP # (P)rimitive execute
[lr1000*li+sr d1-si]sT # (T)hreaded execute
[li d1+ si ;m d500!>P lTx]sL # thread e(L)ement
[lLx li0<J]sJ # (J)oin thread: *inner
## Outer Interpreter ####################
[s_ lk 0;Nx q]sA # (A) interpret number
[s_ 508 l,x lk 0;Nx l,x q]sD # (D) comp number !!! 508=LIT
[s_ ly1+;m 500-;Ox q]sH # (H) interpret primitive
[s_ ly1+;m 1-]sY # (Y) compile primitive
[ly0=A ly=H lr1000*sr dsi 1+ lJx]0:I # (0:I) interpret
[ly0=D ly=Y 1+ l,x]1:I # (1:I) compile
[sx ld sy lQx ly d;m lxly*lc+Z0r^;Ix]sX # (X) lookup word & process
[lXx lhli<Z]sW # (W) X until QUIT
[1si ? lWx]sZ # (Z) *outer interpreter
## initialize... ############################
0sh 0sd 500 l,x 1sc 0sr # initialize mem, interp
0sp [lp:O lEx lh l,x lpd1+sp 500+ l,x]sV # define primiti(V)e
## core prims ###############################
081085073084 [li0r^0r^999*si] lVx # 500 QUIT
078069088084 [lr 1000~ si sr] lVx # 501 NEXT
068079058 [si] lVx # 502 DO:
076065066069076 [lh1-dsh;msk?lExlkl,x] lVx # 503 LABEL
073077077 [ll800r:m] lVx # 504 IMM
091047093 [lc0r^sc4Q] lVx 4;Ox # 505 [/] (IMM)
039 [? sx ld sy lQx ly d;m ly=Y 1+] lVx # 506 ''
044 l, lVx # 507 ,
076073084 [li d 1+si ;m] lVx # 508 LIT
033031 [r:m] lVx # 509 !~
072069082069 [lh] lVx # 510 HERE
074090 [0r^ li;mli-1-*1+li+ 1000% si] lVx # 511 JZ
074077080 [li;msi] lVx # 512 JMP
082062 [ lr 1000~ rsr] lVx # 513 R>
062082 [1000% lr 1000* +sr] lVx # 514 >R
067070065 [ll1+] lVx # 515 CFA
068079069083058 [lr1000/sr s_] lVx # 516 DOES:
## application specific prims ###############
043 [+] lVx # 517 +
045 [-] lVx # 518 -
050042 [2*] lVx # 519 2*
050047 [2/] lVx # 520 2/
060061 [r-2r^Z0r^0r^] lVx # 522 <=
078079084 [0r^] lVx # 523 NOT
064 [;m] lVx # 524 @
068085080 [d] lVx # 525 DUP
068082079080 [s_] lVx # 526 DROP
083087065080 [r] lVx # 527 SWAP
046 [0;Ux] lVx # 528 .
067082 [10P] lVx # 529 CR
## ... and execute ##########################
lZx
EOF
)
########################################################################
# Goedel-code strings for dc (exercise: allow primitive defns in input)
########################################################################
todc=$(cat <<'EOF'
{for(f=1;f<=NF;f++) { print todc($f); }}
END { print todc("QUIT") }
BEGIN {
xtab = " !\"#$%&'()*+,-./"
xtab = xtab "0123456789:;<=>?"
xtab = xtab "@ABCDEFGHIJKLMNO"
xtab = xtab "PQRSTUVWXYZ[\\]^_"
}
function todc(f) {
n=split(f,a,""); s="";
for(i=0;i<n;i++) {
s=sprintf("%s%03d", s, 31+index(xtab,a[i+1]));
}
return s;
}
EOF
)
echo "$boot" "$prog" | awk "$todc" | dc -e "$forthvm"
## QEF ##
--
To unsubscribe: http://lists.canonical.org/mailman/listinfo/kragen-discuss