\*
 *   ZEN 1.10  Input and output
 *     C 1990  by Martin Tracy
 *             Last modified  1.1.90
 *\

VARIABLE SPAN   \ Count of chars from last EXPECT  CORE

\ Read up to +n chars into address or stop at EOL# or with the
\ first non-printing char, which is stored just past the string
: EXPECT ( addr +n) \ CORE
   ( +n) >R  0 ( addr offset)
   BEGIN  DUP R@ <
   WHILE  KEY 127 ( 7-bit ASCII) AND
     DUP [ BSP# ] LITERAL = OVER
         [ DEL# ] LITERAL = OR
     IF    DROP  DUP IF  1-  BACKSP COUNT TYPE  THEN
     ELSE  >R  2DUP +         R@ [ EOL# ] LITERAL -
       IF  R@ OVER C!  THEN   R> BL <
       IF  DROP  SPAN !  R> 2DROP  EXIT
       ELSE  1 TYPE  1+  THEN
     THEN
   REPEAT  SPAN !  R> 2DROP ;


VARIABLE BASE   \ Number conversion base  CORE

\ Decimal number conversion base
: DECIMAL ( ) \ CORE
   10 BASE ! ;

\ Hexadecimal number conversion base
: HEX ( ) \ EXT CORE
   16 BASE ! ;


33 EQU Jot_Size   \ 32 digits in a double number + 1

Jot_Size ALLOT
| THERE LABEL JOT   \ Output conversion area

CHAR A CHAR 9 1+ - EQU A-10

\ Keep together
  VARIABLE DPL      \ Decimal point locator
| VARIABLE 'VAL?    \ VAL? transfer vector
| VARIABLE DIG?     \ True if any digit converted

\ True if the char c is a valid digit in the given base.
: DIGIT ( c base - n t | ? 0)
   SWAP [CHAR] 0 -  9 OVER <  DUP
   IF  DROP [ A-10 ] LITERAL -  10  THEN
   >R  DUP R@ -  ROT R> -  U< ;

\ Convert the char sequence at a+1 and accumulate it in +d.
\ a2 is the address of the first non-convertable digit.
: CONVERT ( ud a - ud2 a2) \ CORE
   BEGIN  1+ DUP >R  C@ BASE @ DIGIT
   WHILE  SWAP  BASE @ UM* DROP
          ROT   BASE @ UM*  D+  DIG? ON  R>
   REPEAT DROP  R> ;

\ String to number conversion primitive.  True if d is valid.
\ Returns d if number ends in final '.' and sets dpl = 0
\ Returns n if no punctuation present   and sets dpl = 0<
| : (VAL?) ( a u - d 2 , n 1 , 0)
   [ Jot_Size 1- ] LITERAL MIN
   JOT 1- OVER -  TUCK >R  CMOVE
   BL JOT 1-  DUP DPL ! C!  DIG? OFF  0 0 R>
   DUP C@ [CHAR] - = DUP >R - 1-
   BEGIN  CONVERT  DUP C@  DUP [CHAR] : =
     SWAP [CHAR] , [CHAR] / 1+ WITHIN  OR
   WHILE  DUP DPL !  REPEAT  R> SWAP >R IF  DNEGATE  THEN
   JOT 1- DPL @ - 1- dpl !   R> JOT 1- =  DIG? @ AND ( valid?)
   IF  DPL @ 0< IF  DROP 1 EXIT  THEN  2 EXIT  THEN
   2DROP 0 ;

\ String to number conversion primitive.  True if d is valid.
: VAL? ( a u - d 2 , n 1 , 0)
 'VAL? PERFORM ;


| CREATE BLANKS   \ 8 contiguous blanks
BL C, BL C, BL C, BL C, BL C, BL C, BL C, BL C,

\ Output one blank
: SPACE ( ) \ CORE
   BLANKS 1 TYPE ;

\ Output n blanks
: SPACES ( n) \ CORE
   BLANKS  OVER 2/ 2/ 2/  0
   ?DO  DUP 8 TYPE  LOOP  SWAP  7 AND TYPE ;


| VARIABLE HLD    \ Output conversion place holder

\ Write a character
: EMIT ( w) \ CORE
   HLD C!  HLD 1 TYPE ;


\ Begin output conversion
: <# ( ) \ CORE
   JOT HLD ! ;

\ End output conversion
: #> ( wd - a u) \ CORE
   2DROP  HLD @  JOT OVER - ;

\ Add character c to output string.
: HOLD ( c) \ CORE
   -1 HLD +!  HLD @ C! ;

\ Add "-" to output string if w is negative.
: SIGN ( n) \ CORE
   0< IF  [CHAR] - HOLD  THEN ;

\ Transfer the next digit of ud to the output string.
: # ( ud - ud2) \ CORE
   BASE @ >R  0 R@ UM/MOD  R> SWAP >R  UM/MOD  R>
   ROT 9 OVER < IF [ A-10 ] LITERAL +  THEN
   [CHAR] 0 + HOLD ;

\ Convert all remaining digits of ud.  ud2 is 0 0 .
: #S ( ud - ud2) \ CORE
   BEGIN  #  2DUP OR  0= UNTIL ;

\ Convert a double number to a string.
| : (D.) ( d - a u)
   TUCK  DABS  <#  #S ROT SIGN  #> ;

\ Type a double number followed by a space.
: D. ( d) \ DOUBLE
   (d.) TYPE SPACE ;

\ Type an unsigned number followed by a space.
: U. ( u) \ CORE
   0 D. ;

\ Type a signed number followed by a space.
: . ( n) \ CORE
   DUP 0< D. ;

\ Print d right-justified in field of width w.
: D.R ( d n) \ EXT CORE
   >R  (D.)  R> OVER - 0 MAX SPACES  TYPE ;