#! /usr/pkg/bin/gforth-fast
\ rpn-n0.cgi - RPN Model n0 calculator CGI script

\ Copyright 2013 David Meyer <[email protected]> +JMJ

\ Copying and distribution of this file, with or without
\ modification, are permitted in any medium without royalty
\ provided the copyright notice and this notice are preserved.
\ This file is offered as-is, without any warranty.

\ Global variables ...

variable register-x
variable register-y
variable register-z
variable register-t
variable register-s
variable mode             \ 0: ENTER mode; next number will replace X
                         \ 1: Op mode; next number will push X
                         \ 2: Input mode; inputing number

variable error

variable query-adr
variable query-len

variable button-adr
variable button-len

\ Level 3 ...

: push-stack ( -- )
   register-z @ register-t !
   register-y @ register-z !
   register-x @ register-y !
;

: rot4 ( a b c d -- d a b c ) swap >r rot rot r> ;

: trunc-fld-key ( c-field ufield ukey -- c-value uvalue )
   dup >r - swap r> chars + swap
;

: value-str-chars ( addr u1 -- u2 )
   over swap [char] & scan drop swap -
;

\ Level 2 ...

: init-state ( -- )
   0 register-x !
   0 register-y !
   0 register-z !
   0 register-t !
   0 register-s !
   0 mode !
   0 button-len !
;

: nprint ( n -- )
   s>d swap over dabs <<# #s rot sign #> type #>>
;

: parse-num-fld { c-key ulen a-reg -- }
   query-adr @ query-len @ c-key ulen search if
       ulen trunc-fld-key
       over swap value-str-chars s>number? if
           d>s a-reg !
       else
           2drop 0 a-reg !
       then
   else
       0 a-reg !
   then
;

: parse-str-fld { c-key ulen a-value a-vlen -- }
   query-adr @ query-len @ c-key ulen search if
       ulen trunc-fld-key
       over swap value-str-chars
       a-vlen ! a-value !
   else
       2drop 0 a-vlen !
   then
;

: pressed-asterisk ( -- )
   register-y @ register-x @ *
   register-x !
   register-z @ register-y !
   register-t @ register-z !
   1 mode !
;

: pressed-clr ( -- )
   0 register-x !
   0 register-y !
   0 register-z !
   0 register-t !
   0 register-s !
   0 mode !
;

: pressed-clx ( -- )
   \ Or should this act like pop/drop?
   0 register-x !
   0 mode !
;

: pressed-enter ( -- )
   push-stack
   0 mode !
;

: pressed-minus ( -- )
   register-y @ register-x @ -
   register-x !
   register-z @ register-y !
   register-t @ register-z !
   1 mode !
;

: pressed-mod ( -- )
   register-x @ 0= if
       true error !
       0 mode !
   else
       register-y @ register-x @ mod
       register-x !
       register-z @ register-y !
       register-t @ register-z !
       1 mode !
   then
;

: pressed-neg ( -- )
   register-x @ -1 * register-x !
   1 mode !
;

: pressed-num ( u -- )
   mode @ case
       0 of
           2 mode !
       endof
       1 of
           push-stack
           2 mode !
       endof
       2 of
           register-x @ 10 * +
       endof
   endcase
   register-x !
;

: pressed-plus ( -- )
   register-y @ register-x @ +
   register-x !
   register-z @ register-y !
   register-t @ register-z !
   1 mode !
;

: pressed-rcl ( -- )
   push-stack
   register-s @ register-x !
   1 mode !
;

: pressed-rld ( -- )
   register-x @
   register-y @ register-x !
   register-z @ register-y !
   register-t @ register-z !
   register-t !
   1 mode !
;

: pressed-slash ( -- )
   register-x @ 0= if
       true error !
       0 mode !
   else
       register-y @ register-x @ /
       register-x !
       register-z @ register-y !
       register-t @ register-z !
       1 mode !
   then
;

: pressed-sto ( -- )
   register-x @ register-s !
   1 mode !
;

: pressed-swp ( -- )
   register-x @ register-y @
   register-x ! register-y !
   1 mode !
;

\ Level 1 ...

: calculate ( -- )
   button-len @ 0<> if
       true case
           button-adr @ button-len @ s" ENTER" str= of
               pressed-enter
           endof
           button-adr @ button-len @ s" mod" str= of
               pressed-mod
           endof
           button-adr @ button-len @ s" clx" str= of
               pressed-clx
           endof
           button-adr @ button-len @ s" clr" str= of
               pressed-clr
           endof
           button-adr @ button-len @ s" swp" str= of
               pressed-swp
           endof
           button-adr @ button-len @ s" %2F" str= of
               pressed-slash
           endof
           button-adr @ button-len @ s" rld" str= of
               pressed-rld
           endof
           button-adr @ button-len @ s" *" str= of
               pressed-asterisk
           endof
           button-adr @ button-len @ s" sto" str= of
               pressed-sto
           endof
           button-adr @ button-len @ s" -" str= of
               pressed-minus
           endof
           button-adr @ button-len @ s" rcl" str= of
               pressed-rcl
           endof
           button-adr @ button-len @ s" neg" str= of
               pressed-neg
           endof
           button-adr @ button-len @ s" %2B" str= of
               pressed-plus
           endof
           button-adr @ button-len @ s>unumber?
           rot rot d>s >r of
               r> pressed-num
           endof
       endcase
   then
;

: parse-query ( -- )
   s" QUERY_STRING" getenv
   dup 0= if
       init-state
   else
       query-len ! query-adr !
       s" s=" register-s parse-num-fld
       s" t=" register-t parse-num-fld
       s" z=" register-z parse-num-fld
       s" y=" register-y parse-num-fld
       s" x=" register-x parse-num-fld
       s" mode=" mode parse-num-fld
       s" button=" button-adr button-len parse-str-fld
   then
;

: print-page ( -- )
   ." Content-Type: text/html"
   cr cr .\" <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
   ." <html><head><title>RPN Calculator Model n0</title>"
   .\" <link rel=\"stylesheet\" type=\"text/css\" href=\"/style/rpn.css\">"
   .\" <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\"></head>"
   .\" <body><h1>RPN Calculator Model n0</h1><form id=\"calc\" method=\"get\" action=\"rpn-n0.cgi\"><div class=\""
   error @ if .\" disperr\">" else .\" disp\">" then
   register-x @ nprint
   .\" </div><table><tr><td colspan=2><input class=\"buttontw2\" type=\"submit\" name=\"button\" value=\"ENTER\" /></td>"
   .\" <td><input class=\"buttontw\" type=\"submit\" name=\"button\" value=\"clx\" /></td>"
   .\" <td><input class=\"buttontw\" type=\"submit\" name=\"button\" value=\"clr\" /></td></tr>"
   .\" <tr><td><input class=\"buttontw\" type=\"submit\" name=\"button\" value=\"-\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"7\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"8\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"9\" /></td></tr>"
   .\" <tr><td><input class=\"buttontw\" type=\"submit\" name=\"button\" value=\"+\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"4\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"5\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"6\" /></td></tr>"
   .\" <tr><td><input class=\"buttontw\" type=\"submit\" name=\"button\" value=\"*\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"1\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"2\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"3\" /></td></tr>"
   .\" <tr><td><input class=\"buttontw\" type=\"submit\" name=\"button\" value=\"/\" /></td>"
   .\" <td><input class=\"buttontw\" type=\"submit\" name=\"button\" value=\"mod\" /></td>"
   .\" <td><input class=\"buttonwb\" type=\"submit\" name=\"button\" value=\"0\" /></td>"
   .\" <td><input class=\"buttontw\" type=\"submit\" name=\"button\" value=\"neg\" /></td></tr>"
   .\" <tr><td><input class=\"buttonbw\" type=\"submit\" name=\"button\" value=\"rld\" /></td>"
   .\" <td><input class=\"buttonbw\" type=\"submit\" name=\"button\" value=\"swp\" /></td>"
   .\" <td><input class=\"buttonbw\" type=\"submit\" name=\"button\" value=\"sto\" /></td>"
   .\" <td><input class=\"buttonbw\" type=\"submit\" name=\"button\" value=\"rcl\" /></td></tr></table>"
   .\" <div class=\"stat\">"
   .\" S<input readonly name=\"s\" value=\""
   register-s @ nprint
   .\" \" /><br />"
   .\" T<input readonly name=\"t\" value=\""
   register-t @ nprint
   .\" \" /><br />"
   .\" Z<input readonly name=\"z\" value=\""
   register-z @ nprint
   .\" \" /><br />"
   .\" Y<input readonly name=\"y\" value=\""
   register-y @ nprint
   .\" \" /><br />"
   .\" X<input readonly name=\"x\" value=\""
   register-x @ nprint
\    .\" \" /><input type=\"hidden\" name=\"input\" value=\""
\    input @ nprint
   .\" \" /><input type=\"hidden\" name=\"mode\" value=\""
   mode @ nprint
   .\" \" /></div><div class=\"label\">RPN CALCULATOR n0</div></form>"
   .\" <div id=\"inst\">"
   ." <h3>Instructions</h3>"
   .\" <p class=\"instp\">Enter numbers separated by "
   ." ENTER key, then press operation key to display the result "
   ." (= key is not needed). Numbers are stored in a "
   ." LIFO stack (registers X, Y, Z, T). Display shows the last "
   ." number (input or result) on the stack (register X). "
   ." Register S is for storing constants.</p>"
   .\" <p class=\"instp\"><strong>Stack effects:</strong> "
   ." (<em>x, y, z, t, s,</em> are current register values.)</p>"
   .\" <table><tr><td></td><td class=\"instblk\"><em>op</em></tr>"
   .\" <tr><td class=\"instblk\"><input class=\"buttontwj\" type=\"button\" disabled value=\"ENTER\" /></td>"
   .\" <td class=\"instblk\">(<input class=\"buttontwj\" type=\"button\" disabled value=\"+\" /><input class=\"buttontwj\" type=\"button\" disabled value=\"-\" /><input class=\"buttontwj\" type=\"button\" disabled value=\"*\" /><input class=\"buttontwj\" type=\"button\" disabled value=\"/\" /><input class=\"buttontwj\" type=\"button\" disabled value=\"mod\" />)</td>"
   .\" <td class=\"instblk\"><input class=\"buttontwj\" type=\"button\" disabled value=\"neg\" /></td>"
   .\" <td class=\"instblk\"><input class=\"buttonbwj\" type=\"button\" disabled value=\"sto\" /></td>"
   .\" <td class=\"instblk\"><input class=\"buttonbwj\" type=\"button\" disabled value=\"rcl\" /></td></tr>"
   .\" <tr><td class=\"instblk\"><em>z</em> &rarr; T<br /><em>y</em> &rarr; Z<br /><em>x</em> &rarr; Y,X</td>"
   .\" <td class=\"instblk\"><em>t</em> &rarr; T,Z<br /><em>z</em> &rarr; Y<br /><em>y op x</em> &rarr; X</td>"
   .\" <td class=\"instblk\"><em>-x</em> &rarr; X</td>"
   .\" <td class=\"instblk\"><em>x</em> &rarr; S</td>"
   .\" <td class=\"instblk\"><em>z</em> &rarr; T<br /><em>y</em> &rarr; Z<br /><em>x</em> &rarr; Y<br /><em>s</em> &rarr; X</td></tr></table>"
   .\" <table><tr><td class=\"instblk\"><input class=\"buttonbwj\" type=\"button\" disabled value=\"rld\" /></td>"
   .\" <td class=\"instblk\"><input class=\"buttonbwj\" type=\"button\" disabled value=\"swp\" /></td>"
   .\" <td class=\"instblk\"><input class=\"buttontwj\" type=\"button\" disabled value=\"clx\" /></td>"
   .\" <td class=\"instblk\"><input class=\"buttontwj\" type=\"button\" disabled value=\"clr\" /></td></tr>"
   .\" <tr><td class=\"instblk\"><em>x</em> &rarr; T<br /><em>t</em> &rarr; Z<br /><em>z</em> &rarr; Y<br /><em>y</em> &rarr; X</td>"
   .\" <td class=\"instblk\"><em>x</em> &rarr; Y<br /><em>y</em> &rarr; X</td>"
   .\" <td class=\"instblk\">0 &rarr; X</td>"
   .\" <td class=\"instblk\">0 &rarr; X,Y,Z,T,S</td></tr></table>"
   .\" <p class=\"instp\"><strong>Precision and Fractional Arithmetic:</strong> "
   ." n0 processes all numbers as single-precision signed integers with a "
   ." range of -2,147,483,648 to 2,147,483,647. "
   ." It is possible to perform calculations with fractional "
   ." numbers by using the technique of "
   ." <strong>fixed-point arithmetic</strong>: The user multiplies input "
   ." operands and mentally divides results by appropriate powers of 10 to "
   ." obtain the required precision.</p></div>"
   .\" <p><a href=\"rpn-n0-cgi.fs\">Program source.</a></p>"
   ." <p>Model n0 is the first of a series of online "
   ." calculators inspired by the Hewlett-Packard "
   ." line of slide rule pocket calculators "
   ." produced in the 1970s (n0 was designed "
   ." with refrence to the "
   .\" <a href=\"http://www.hpmuseum.org/hp35.htm\">HP-35</a> "
   ." in particular) and the "
   .\" <a href=\"http://www.forth.org/whatis.html\">"
   ." Forth programming language</a> invented by "
   .\" <a href=\"http://www.colorforth.com/bio.html\">"
   ." Chuck Moore</a> in 1968.</p>"
   ." <p>RPN Calculator Model n0 is powered by "
   .\" <a href=\"http://bernd-paysan.de/gforth.html\">Gforth</a> "
   s" gforth" environment? if type space then
   ." on the MetaArray host at "
   .\" <a href=\"http://www.sdf.org\">SDF</a>.</p>"
   .\" <p class=\"ctr\"><a href=\"http://www.catholic.org/clife/prayers/prayer.php?p=1378\">+JMJ</a></p></div></body></html>"
;

\ Level 0: Main driver ...

false error !

parse-query
calculate
print-page
bye


\ Emacs metadata ...

\ Local variables:
\ mode: forth
\ End:

\ +JMJ