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

\ Copyright 2013 David Meyer <papa@sdf.org> +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