From c0f19f767433281363beada1bc7b740a6556b16b Mon Sep 17 00:00:00 2001 From: Jeremy Penner Date: Wed, 30 Jan 2019 22:12:28 -0500 Subject: [PATCH] Forp: Refactor interpreter, inline VM loop, implement if/then and immediate words --- forp.c | 212 ++++++++++++++++++++++++++++++++++++------------------- forp.exe | Bin 34447 -> 35752 bytes 2 files changed, 141 insertions(+), 71 deletions(-) diff --git a/forp.c b/forp.c index 4876719..3be75fe 100755 --- a/forp.c +++ b/forp.c @@ -28,6 +28,8 @@ cell *stack = stack_mem; char *INPUT = NULL; +#define F_IMMEDIATE 0x80 + #define CELL_OFFSET(cp, b) ((cell*)(((char *)(cp)) + b)) #define TOP() (*(stack - 1)) #define ST1() (*(stack - 2)) @@ -88,7 +90,6 @@ void f_rot() { // a b c -- b c a ST2() = b; } - void f_key() { if (INPUT) { PUSHI(*INPUT); @@ -166,23 +167,26 @@ void f_cdef() { // func name -- DROP(1); } - -int RET = 0; void f_docolon() { - RPUSH(IP); - IP.p = W.p + 1; + RPUSH(W); - while (!RET) { + IP.p = W.p + 1; + while (rstack != rstack_mem) { + cell codeptr; W = *IP.p; IP.p++; - W.p->f(); + codeptr = *W.p; + if (codeptr.f == f_docolon) { + RPUSH(IP); + IP.p = W.p + 1; + } else { + codeptr.f(); + } } - RET = 0; - IP = *RPOP(); } -void f_ret() { - RET = 1; + +void f_noop() { } void f_donumber() { @@ -203,63 +207,100 @@ void f_streq() { TOP().i = result == 0; } -void f_lookup() { // name -- code pointer +void f_lookup() { // name -- (codepointer flags) | (name 0) cell *entry = LATEST; while (entry) { f_dup(); PUSHP(entry + 2); f_streq(); if (TOP().i) { - DROP(1); - TOP().p = CELL_OFFSET(entry + 2, entry[1].u + 1); + unsigned int flags = entry[1].u; + TOP().u = flags; + ST1().p = CELL_OFFSET(entry + 2, (flags & ~F_IMMEDIATE) + 1); return; } DROP(1); entry = entry->p; } - TOP().p = NULL; + PUSHU(0); } -void f_def() { - cell *herebefore = HERE; - cell *latestbefore = LATEST; +void f_compileon() { + STATE.i = 1; +} +void f_compileoff() { + STATE.i = 0; +} + +void f_immediate() { + cell *flags = LATEST + 1; + flags->u |= F_IMMEDIATE; +} + +void f_semicolon() { + PUSHS("_RET"); + f_lookup(); + DROP(1); + f_comma(); + f_compileoff(); +} + +void f_ret() { + IP = *RPOP(); +} + +void f_colon() { f_word(); f_create(); PUSHP(f_docolon); f_comma(); - while(1) { - f_word(); // w - f_dup(); // w w - f_lookup(); // w code - if (TOP().p == NULL) { - DROP(1); // w - f_dup(); // w w - f_number(); // w n isnum - if (TOP().i) { - PUSHS("_DONUMBER"); - f_lookup(); // w n isnum _donumber - f_comma(); - PUSHI(ST1().i); // w n isnum n - f_comma(); // w n isnum - DROP(3); - } else { - DROP(2); // w - HERE = herebefore; - LATEST = latestbefore; - f_puts(); - PUSHS("?\n"); - f_puts(); - f_cr(); - return; - } - } else if (TOP().p->f == f_ret) { - f_comma(); - DROP(1); - return; + f_compileon(); +} + +void f_interpretword() { // codefield flags -- + if (!STATE.i || (TOP().u & F_IMMEDIATE)) { + DROP(1); + W = TOP(); // w code + DROP(1); + W.p->f(); + } else { + DROP(1); + f_comma(); + } +} + +void f_interpretnumber() { // number -- + if (STATE.i) { + PUSHS("_DONUMBER"); + f_lookup(); + DROP(1); + f_comma(); + f_comma(); + } +} + +void f_interpretunknown() { // name -- + f_puts(); + PUSHS("?\n"); + f_puts(); +} + +void f_compileword() { // name -- + f_lookup(); + if (!TOP().u) { // name 0 + DROP(1); // name + f_dup(); // name name + f_number(); // name n isnum + if (TOP().i) { + f_rot(); // name n isnum -- n isnum name + DROP(2); // n isnum w -- n + f_interpretnumber(); } else { - f_comma(); - DROP(1); + DROP(2); // name + f_interpretunknown(); } + } else { // codepointer flags + f_interpretword(); } } @@ -273,26 +314,7 @@ void f_interpret(char *input) { DROP(1); return; } - f_dup(); // w w - f_lookup(); // w code - if (TOP().p == NULL) { - DROP(1); // w - f_dup(); // w w - f_number(); // w n isnum - if (TOP().i) { - f_rot(); // w n isnum -- n isnum w - DROP(2); // n isnum w -- n - continue; - } - DROP(2); // w - f_puts(); - PUSHS("?\n"); - f_puts(); - return; - } - W = TOP(); // w code - DROP(2); - W.p->f(); + f_compileword(); if (stack < stack_mem) { stack = stack_mem; PUSHS("stack underflow!\n"); @@ -302,6 +324,45 @@ void f_interpret(char *input) { } } +void f_if() { + if (STATE.i) { + PUSHC(W); + f_comma(); + PUSHP(HERE); + PUSHP(NULL); + f_comma(); + } else { + if (TOP().u) { + IP.p ++; + } else { + IP.p = IP.p->p; // skip to then + } + DROP(1); + } +} + +void f_else() { + if (STATE.i) { + + PUSHC(W); + f_comma(); + + TOP().p->p = HERE + 1; + TOP().p = HERE; + + PUSHP(NULL); + f_comma(); + + } else { + IP.p = IP.p->p; + } +} + +void f_then() { + TOP().p->p = HERE; + DROP(1); +} + int DIE = 0; void f_quit() { DIE = 1; @@ -309,12 +370,19 @@ void f_quit() { #define CDEF(name, def) PUSHP(def); PUSHS(name); f_cdef() void f_init() { - CDEF(":", f_def); - CDEF(";", f_ret); + CDEF("[", f_compileoff); f_immediate(); + CDEF("]", f_compileon); + CDEF("immediate", f_immediate); + CDEF("create", f_create); + CDEF(":", f_colon); + CDEF(";", f_semicolon); f_immediate(); CDEF("+", f_add); CDEF("-", f_sub); CDEF("@", f_get); CDEF("!", f_set); + CDEF("if", f_if); f_immediate(); + CDEF("else", f_else); f_immediate(); + CDEF("then", f_then); f_immediate(); CDEF("dup", f_dup); CDEF("drop", f_drop); CDEF("swap", f_drop); @@ -322,9 +390,11 @@ void f_init() { CDEF("emit", f_emit); CDEF("number", f_number); CDEF("_DONUMBER", f_donumber); + CDEF("_RET", f_ret); CDEF(".", f_dot); CDEF(".s", f_puts); CDEF(",", f_comma); + CDEF("noop", f_noop); CDEF("quit", f_quit); f_interpret(": -rot rot rot ;"); } diff --git a/forp.exe b/forp.exe index a57a2e90eb7b6029edaf3d954000f0e5ca447e93..ee516466f09bee1d8c2ebe3da8f1205b47d407ab 100755 GIT binary patch delta 8289 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

g_J{+dq6-{Q1$nQG+k=#2b5 z4m>)Ym4Be~axKn0SLl*_mA;pMqMziSarF5&{VHFl2Kg5v6RUi_(@=DDBubr9FE=xr^;m z64)zB5_?tYz$%rF>#q@R;_en?CL`W z`m%47e(bW+pXo|6`%xLlt|}?)7iAE;p$uk%I)qu(ds%aJC~K(>XRXv!7O#$AiE0|_ zqK;&J)KTnibu1gGrn3}v92=tE$I{gCY@B*OyI;*HdCF*W~-A}mYT`()X6Mg zox&EX)7X>hbXKfB$VyZP^Qg1fI`tv8S)Ie4RkPSmbuN2doyQKT+3bj#!;Y$tvXg2a zt5zRpHR==WV|6||uNJTi>O%H~x`=(FI@x8_#dLKE3#dgbCSob;9PuO@6yaXR#z!n? z4@azEizA+5Ya)u-wg@+?j3{9zBTCu%h?VT82oGb{RjieDHS1_E18G5KDMklLh)D=zj|7S z1|AAtA@@XRUez)``kM!Pd@nLZgdc?T5#Y0T=oe%g{TY5nLcb)VheFqNxt&3y5&nui zynf?qu9%s|A{MRO?#(2njgg*?u#>_M)#f!s;Y~CT|HBl1Pv+%R_`%tH1t?MyO%Y*5 z$UlD(*mm2$8}CiLjppk>rIJd2L96-oqmn^ygZWFrRfWG}m{*EA-hinHhp4<+=2fDO zG2j7&9@X^8g=*Do^bBx~db=kssJut>1EYw4Du}Kx~en|_%&+}I*ta- zhJM4^5hKiJ|0R{-4I*?L!G8~?jeF`Sdqoq9AJTG*lUR4{>Eg-S{^BIA{P$a|zK^QW0I?f(CjbBd delta 7081 zcmb`Ld3;mF+Q*+cYg%YaDNR!_0Sbj8n^GxYrL-(*QI=53QiT*Ng^CE+CV&V;2wNIa z@gSfgf>o@5Z0*H>Ti+M2=&K?KR}r~--46yY?n_CN_cteP(B9AI{p&sXJafMDn|WsD z%$YOKv~^pBhc*gH<8LK4ghc8_M3G3{p`;%fE6@bt)&b|lRS^=Z)z?Vy z=A9C&G7d>+E*Iuru1fc!fLy9K@03;}>@#mBU*Z+pD`hmET+Vj3lZ~d*qr+QVlwj_CcjhT&2c_NyFxh2j6h4L;7WPi<)$ zJ^tx2BTK>_y(wI3xO*>2(UFBPvM6DUA%pB8UttS{(Oqj}my}q&77zGF>lHTn>`hVM zChlQODtxcD*JQNP%YVt6*FfaT@Y@=wM&0JnSA$Pm3+ zdr!3`N5}hh9EKNtqgq%sd-6lAlNMc~ktN3K#M9^vsimdS6^)1H(=7)W1%>QJKD}3$ z%(!v8_7Lg?N9rGi{PwUcVFHsvrTQM@Y5Dthb&gJhGmLVvP>^L~DWDBc@S0eW$~)uz z1b7E03d2OPRViUcQEc=S(g!x{eZqk|WrtpOrBoNfx~*AvhV-gfhi<3Qsy;TPe{L*P zV-!6C-(YAP;8GOP-)ZK(c<{V>9+X#`xrY+$$!`*diFfC>hidnbes6Q&2l!1wtp&~c z+U8)dV0Z58FAJ|4aX@yr*H;9$u0^b!qBPP1y^_Q*~0}PHaZu%#o!0%}3NK>hPCKSvz zhE*8mE5>*XW5uk>J4`#qRHvN4ez;>y;0o%mL;c!NP`v1=jI=D8bFGUeuvc;DlbcEd z3Cy88QL$@t-~gsNA5s#eTOhS>4(t^idVfgCkcL9~BNTWSQfEjhkTM~C9SU5Aq(VxC z)DhB|P(TtL`p zQfSw>Jkf8f5+X{KP@^_b&=jcD97(;q%o940OG590|3sOyWwizh5*&f0+9OHXSx&q6 zrpSsRektE4`DesOM#t}T+Bc}bjTTJgvfiKPw8y(t+_dkKN<3Ff(yx}-6Wr~l>OymA zyGEA~d^A{O6uG;iiF|Tfu$?#p?~9I_(Gp^WsS|dl;jC46+FSJRQ=+;n9&RMK#f#+; z45Wda#>$=QUn~Wd);D&@V`cLNv}2ahom*MeYn~8k4v^sZO_>KjSL^QO$Zm85DzPqy zlJ|Af&gISFPtNj&|)Pdp4n5m@UzaY1n)R;jFaFxBr)zg~8P0O*cXe69SdNx%UEHrKo&RQa}jE0z0BTpD~uLsQ)Qr9?Q+Jwzp2RrdiioAF-aYerUa5&9}<7FRb5KgVt5H z{#{Soez48$R%`qQS%+HrvCt`<{^Gawb=tO`13h&jb(kK+x8457na7XWFA;u~m`m~H6wASGxjE1h{GaG0IQ9s&kdVo4taZq>9g5}J5_z&-efhTIk)&Lk zLz|H~+*~62mqf}G?!?1j|Vr^Eh)K1kcve|!bSF!9X6nKHJ zS7l`wBj5mh`6=Qp)TuVr1P%8_2vVYk9&U>vQNP@Sl4?)}U)<@R!WtH$p;TIx9E z9b-04>ZL8qO1y6L#0gWzj~r7>BjRb{UGe-@?YjMrySlj-%&w@KGk@Wd?(uXZtIi4Zu-?7%Sg~{76BH z9kyt*^CjaKuyvL|??y6wq^)~t6+vH6~-&Z@Zw8>|+$q#LlFN)=hql26t6QcRb zHu?|Npsx%))pSATtfPr;^HDcZpWlI=l-=mRPt znU2sV>Vy79(=RwE@E_f+y}!Wj8AZeJz!npYq;VYLsDR2i#8VzsacD>Rw3b7AggZX{ zmIS(<_Hsz1ar7>SB)X0+bLc<=Ny30F$uySu)L1&wEi{Nj3f9F)4xOl&TpUtqfQOE8 zGE)&<=8#4csS0n`VxdX&0Ecucq}?1k(;#Z((1q-Dio-QDoGx*&(qy_BE7oG8VRR>l zu2_NxICP_%=op9YG=@%c$e^1k853jafu-sh%c&>zqrDt5X(-KA0ljDf#hU=VX*{KJ z$fChifyHa#)A%5Ou7%Ze=nMORLqFK`cqIK{KXVuWo7xV^K+2-69Im5Y6yb0^%;i`&==XFIU?}V|hhebx9gy4z>&YP(mX?g^UwKz&3V7G7`3n!>Cpm4Y(x*$rxA(2Rm#NheFuX9ExCX za~R7rosirNTgc%Sn2*Ca>P`DOjEB9~iO~N9(3(^v6Ja|zIAAk6BbkKSA9n#11LCg% zOorXWp#&y&M=}Mrh{IIahzumtU|V`1nGXAgLn$oC;a1pX4!6O60(fW!2#+q%Ox&tC zI01_}lmV7;m<3qL;da1k4&{I~9PR+D<1ibrfkOqLhQl1d103c89^_C7c$mYTt>Bpl z+Qg;10FQ8(57^A%ZooDU3jmLESP0n8VG&>_hkF3eaHzryt>aLQrLu>Ei!8K{gB!4) z!(z4;<#O)PptG!0BF+uvGw-0nTx_4^2B6U^!qGhZTTw0k9GHco0S5^CBUheiQB(~cf4&|i7FUk}M8gZiO8e_|=&xBUKwP~4)~mi1TJ8;P z@_%cSKhh@uVw-%RP5x+`{4wPD<`TPJije{J=iP^xXEUX}IDA4+gvm@q$X*DJy%87t zAS7OkXxJZtZ~z??2GTL%I(kjGp579&5fg8q_l3c9LCB%cgdzAFWEjHZjr60COM*BY z5i*bB#1WJt=95J%pq}C=1k2IXR~$pviG_%lMKo9(OZnn09>mOXR3wh4VsQe^5GT?c z(Lswu9zrM6GO>i#h*Ri6aVl*Sr_s~mblNAD(h+e60_#kAMRd~ZVi`WfS#(ysojwuo zpfAPQ^tCvLz7s1EbLY`7;$74vE+8f?BuQFCiPGJaBDtuyk+j#(DRayj!HH3s`LPY z_eMG{JxFg$579|!6Jq!ybVkzXD`_)*D{Vn2-$p-3k5QBKI6l`WNX93d;*p=E?(z;A zB0oh1@=mhLPb1XV(s=n9>>SV1OnEnz%g@nVc`qXWKAJDTKrZ9Bl^0`ki^8@xiV$gk1~`89e+{x2L6{+rIq$LS;ab^1a6 zJ9)0iZ%~5rCXNhmky-g4>ZiO-*DLSP80935RoW?I;KT(qUGj&vdp)~bZ zN>_g)oBBKTRR5rB)h6n%UZKHih=!`ol&eN)xJqoC%Gg9zU?r-^rm7N~uFC8-)x_>l z(yjdqjqEush!v+HI;2v&1|ch#&)R|wp&eS&#Rr; zKD7%wqF%#}s#f-@+Lbk`-PqsN?(9u9gPl@)urq2V`-j?#eX90mU#eN`TeT1SQN5P^ zuJ&bQ>c>RWKxQ%xV)3SImSDPprI?1WRMSvq@tB6OzNQ;lo++12GTp>xn})L`raV?- z8o{14<+FpP0(RUql6_zr#V(pgvyf>FONz6zjJQHJIIf5l#f@dR#of&Ah#SW$;wG^9 zag*3RaSrB*o6Oe5m9Uz)NBChF*DxW4G-LO|5d;5Q*=*?8pvIv9n;VI!G!X?`S{%xt zZw1TH%b{;;(W{_82F5v(+|VCy(bqzM0?bc}8=!A*(YHZ=63h>UPe5Rg=!x{OjKglrbxh_bwdk?*6`7w9qt{9J?m?e}kv%K(qh<6; zR)*rl!4REsvQ@HMWqc;(^aAu@e7B#V@Pk(L+N->blZjxQR}U#i5Hg~#Vy;?Chrqn) zdFsxV+6Yjl=s-t6NibaG&l2+pVL3`3`GUK)5^uW&tc+r^c$EY*i z|G4H|^Q%}@;nG&D?cZT`#T+V|Q+DS9S4Gt#i1ky}?GY?h#vjR4)^Zy=!7-(T%8JL- wr~8UnR#io{d%lYnxbLQl1+J=lY5wi5ifUJR#hisz6;xSKRT0H{PfZ{8zY6TpkN^Mx