13.12 Numberio Interface

ORIGIN 'betaenv';
LIB_DEF 'numberio' '../lib';
BODY 'private/numberioBody'
(*
 * COPYRIGHT
 *       Copyright (C) Mjolner Informatics, 1992-96
 *       All rights reserved.
 *
 * This fragment implements the following stream operations:
 *    getNumber  * reads a number from THIS(stream).
 *               * The number may be either an integer, 
 *               * a based number or a real number
 *    getBased   * reads a based number from THIS(stream)
 *    getRadix   * reads a based number from THIS(stream), 
 *               * without the 'bbx' part
 *    getInteger * reads an integer number from THIS(stream)
 *    getReal    * reads a real number from THIS(stream)
 *    putReal    * appends a textual rep. of a real value to 
 *               * THIS(stream)
 *    putBased   * appends a textual rep. of a integer value in a 
 *               * particular to THIS(stream).  The textual
 *               * representation in in the given base
 *    putRadix   * as putBased, except that the 'bbx' part is
 *               * not printed
 *    getHex     * similar to getRadix with radix 16, but more efficient.
 *    putHex     * similar to putBased with base 16, but more efficient.
 *    getOctal   * similar to getOctal with base 8, but more efficient.
 *    putOctal   * similar to putBased with base 8, but more efficient.
 *    getBinary  * similar to getRadix with radix 2, but more efficient.
 *    putBinary  * similar to putBased with base 2, but more efficient.
 *    putByteHex    * like putHex, except that it only prints one byte.
 *    putByteBinary * like putBinary, except that it only prints one byte.
 * 
 *    asNumber   * abstract pattern for the following asBased,
 *               * asRadix, and asReal operations
 *    asBased    * returns the based number present in
 *               * THIS(stream)
 *    asRadix    * returns the based number present in
 *               * THIS(stream), without the 'bbx' part
 *    asReal     * returns the real number present in
 *               * THIS(stream)
 * 
 * The corresponding short-cuts for keyboard.getNumber, etc, and 
 * screen.putReal, etc. are also included in this fragment.
 * 
 * Since the asNumber operations does not make sence for keyboard,
 * no short-cuts are defined for these.
 *)
--- StreamLib: attributes ---
getNumber:  
  (* getNumber reads a number from the current position of 
   * this(stream).
   * The number is either an integer (in base 10), an integer with a
   *  given base, or a real.
   * Integer examples:  10, 0, 123
   * A based integer has the form <base>X<number>. Examples are:
   *    2X101          base=2, number= 4*1 + 2*0 + 1*1 = 5
   *    8X12           base=8, number= 8*1 + 1*2 = 10
   *    16x2A1         base=16, number= 256*2 + 16*10 + 1*1  = 673
   *     0x2A1         base=16, i.e. base=0 is interpreted as base=16
   * Examples of reals are:
   *   3.14, 3.14E-8, 3E+8
   * The following grammar defines the exact syntax of the numbers:
   *
   * N ::= {D}+                        Int             314
   *    |  {D}+ '.' {D}+               real            3.14
   *    |  {D}+ '.' {D}+ 'E' E         real            3.14E8
   *                                   real            3.14E+8
   *                                   real            3.14E-8
   *
   *    |  {D}+  'E' E                 real            3E8
   *                                   real            3E+8
   *                                   real            3E-8
   *    |  'X' {D | L}+                based           2X0101
   *                                   based           8x0845
   *                                   based           16xAF12
   * D ::= {'0' | ... | '9' }
   * L ::= {'A' | ... | 'Z'}
   * E ::= {D}+
   *    |  {D}+ '+' {D}+               
   *    |  {D}+ '-' {D}+
   *    
   * All letters may be in lower or upper case.
   * After the call, the stream is positioned
   * after the first char not in the number.
   *)
  (# integerValue:<
       (* the number has the form
        *     x
        * value contains the integer value
        *)
       integerValuePtn;
     integerValuePtn:
       (# value: @integer enter value do INNER #);
     basedValue:<
       (* the number has the form
        *     bXy
        * base contains the base number
        * value contains the integer value (in base 10)
        *)
       basedValuePtn;
     basedValuePtn:
       (# base,value: @integer enter (base,value) do INNER #);
     realValue:<
       (* the  number has the form 
        *       x.yEz
        *       l is the number of leading zero's in y. i.e. in
        *       3.0017E-12, x=3, y=17,l=2 and z=-12
        * value contains the real value
        *)
       realValuePtn;
     realValuePtn:
       (# x,y,l,z: @real; value: @real enter(x,y,l,z,value)
       do INNER #);
     syntaxError:< streamException
       (# peekCh: @char
       enter peekCh
       do 'getNumber: Syntax error - looking at: "'->msg.append;
          (if peekCh = ascii.nul then 'NUL'->msg.puttext
           else peekCh->msg.put if);
          '"'->msg.put; INNER #);
     baseError:< streamException
       (# base: @integer
       enter base
       do 'getNumber: Error in base - looking at: "'->msg.append; 
          base->msg.putInt; '"'->msg.put; INNER #);
     valueError:< streamException
       (# peekCh: @char
       enter peekCh
       do 'getNumber: Illegal value type - looking at: "'->msg.append;
          peekCh->msg.put; '"'->msg.put; INNER #);
     overflow:< streamException
       (# value: @integer
       do 'getNumber: Overflow in integer- or based-value'->msg.append;
          INNER
       exit value
       #);
     underflow:< streamException
       (# value: @integer
       do 'getNumber: Underflow in integer- or based-value'->msg.append;
          INNER
       exit value
       #);
     EOSError:< streamException
       (# 
       do 'getNumber: End of stream while reading number'->msg.append; 
          INNER 
       #);
     getn: @...
  do getn;
     INNER getNumber
  #);
getReal: getNumber
  (# r: @real;
     realValue::< (# do value->r #)
  do INNER getReal
  exit r
  #);
getBased: getNumber
  (# i, b: @integer;
     basedValue::< (# do value->i; base->b #)
  do INNER getBased
  exit (b,i)
  #);
getInteger: getNumber
  (# i: @integer;
     integerValue::< (# do value->i #)
  do INNER getInteger
  exit i
  #);
getRadix:
  (* gets a number in the specified radix.  GetRadix is similar to
   * getBased, except that is does NOT expect the 'bbx' prefix
   *)
  (# radix, value: @integer;
     radixError:< streamException
       (# radix: @integer
       enter radix
       ...
       #);
     getr: (* private *) @...
  enter radix
  do getr;
     INNER getRadix
  exit value
  #);

putBased:
  (* Takes a number and a base, and prints the number in that base.
   * If base is 0, base 16 is assumed, and the format "0xnnn" is used.
   * If base is negative, 1 or greater that 126, the baseError
   * exception is invoked.
   * 
   * The format is default "bbxnnnn", where "bb" is the base (in
   * decimal), and "nnnn" is the number, printed in the base. "x"
   * separates the two parts.  The format may be controlled by the
   * signed, blankSign, upcase, uppercase, width, adjustLeft,
   * zeroPadding, noBasePrefix, baseWidth and baseZeroPadding variable
   * attributes. If noBasePrefix is true, the "bbx" part is omitted.
   *)
  (# value, base: @integer;
     baseError:< streamException
       (# base: @integer
       enter base
       do 'putBased: Illegal base: "'->msg.append; 
          base->msg.putInt; '"'->msg.put; INNER #);
     (* The format may be further controlled by the signed, blankSign,
      * width, adjustLeft and zeroPadding variable attributes.  
      * width is extended if it is too small.
      *
      * Examples:
      *   (10,10)->putBased
      *      yields: '10x10'
      *   (2,5)->putBased(# do 10->width; true->adjustLeft #);
      *      yields: '2x101     '
      *   (2,5)->putBased(# do 10->width; true->zeroPadding #);
      *      yields: '2x00000101'
      *)
     signed:
       (* If the number is positive, a '+' will always be displayed
        *)
       @boolean;
     blankSign:
       (* If the number is positive, a ' ' space is displayed as the
        * sign.  Ignored if signed=true
        *)
       @boolean;
     upcase: @boolean
       (* Specifies whether an upcase 'X' or a lowcase 'x' is the
        * be used in the 'bbx' part.
        *);
     uppercase: @boolean
       (* Specifies whether uppercase letters or lowercase letters
        * are used in the 'nnnn' part (for base>9).
        *);
     width: (* Minimum width *) @integer;
     adjustLeft: @boolean
       (* Specifies if the number is to be aligned left or right,
        * if padding of spaces is necessary to fill up the specified
        * width.
        *);
     zeroPadding:
       (* width is padded with leading zero instead of spaces.
        * Ignored if adjustLeft=true
        *)
       @boolean;
     noBasePrefix: (* If true, the 'bbx' part is omitted *)
       @boolean;
     baseWidth: (* minimun width for the 'bbx' part *)
       @integer;
     baseZeroPadding:
       (* baseWidth is padded with leading zero instead of spaces *)
       @boolean;
     format:< (# do INNER #);
     putb: @...
  enter (base, value)
  do INNER putBased; putb
  #);
putReal:
  (* Append a real to THIS(stream). The format may be controlled by
   * the style, signed, blankSign, precision, upcase, width,
   * adjustLeft and zeroPadding variable attributes
   *)
  (# r: @real;
     style: @integer
       (* Controls the style, and may be one of plain, exp and noexp
        * (noexp is the default)
        *);
     noexp: (* The notation [-]mmm.dddddd is used *)
       (# exit 0 #);
     exp: (* The notation [-]m.ddddddE[+|-]xx is used *)
       (# exit 1 #);
     plain:
       (* In this style, precision is the total number of digits in
        * the printed real (not the number of digits in the fraction,
        * as in the other styles).
        * 
        * The exp or noexp style is used, dependent on the value being
        * printed.  Exp style is used only if the exponent is less
        * than -4 or greater than or equal to the precision; otherwise
        * the noexp notation is used.  Trailing zeros are not printed
        * as part of the fractional part and a decimal point is
        * printed if not followed by a digit
        *)
       (# exit 2 #);
     signed: (* If real is positive, a '+' will always be displayed *)
       @boolean;
     blankSign:
       (* If real is positive, a ' ' space is displayed as the sign.
        * Ignored if signed=true
        *)
       @boolean;
     precision: @integer
       (* The number of d's in the expressions above, default 6 *);
     upcase: @boolean
       (* Specifies whether an upcase 'E' or a lowcase 'e' is the
        * be used in the exp style.
        *);
     width: (* Minimum width *)
       @integer;
     adjustLeft: @boolean
       (* Specifies if the number is to be aligned left or right,
        * if padding of spaces is necessary to fill up the specified
        * width.
        *);
     zeroPadding:
       (* width is padded with leading zero instead of spaces.
        * Ignored if adjustLeft=true
        *)
       @boolean;
     (* Examples:
      *   10*pi -> putreal;
      *      yields: '31.415926'
      *   10*pi -> putreal(# do 10->width; true->adjustLeft #);
      *      yields: '31.415926   '
      *   10*pi -> putreal(# do exp->style; true->upcase #);
      *      yields: '3.1415926E+01'
      *   10*pi -> putreal(# do exp->style; 2->precision #);
      *      yields: '3.14e+01'
      *)
     format:< (# do INNER #);
     putr: @...
  enter r
  do 1->width; 6->precision; format; INNER putReal; putr
  #);
putRadix: putBased
  (#
  do true->noBasePrefix; INNER putRadix 
  #);

putHex:
  (* prints a hexadecimal representation of x (as unsigned word) on
   * this(stream). Similar to
   * (16,x)->putRadix(# do true->zeroPadding; 8->width #)
   * but more efficient.
   *) 
  (# uppercase: @boolean;
     width: @integer;
     zeroPadding: @boolean;
     x: @integer;
     format:< (# do INNER #);
     putH: (*private*)@...
  enter x
  do 8->width; true->zeroPadding; format; INNER putHex; putH
  #);

putByteHex:
  (* prints a hexadecimal representation of byte 'byte' in x (as
   * unsigned word) on this(stream)
   *)
  (# x: @integer;
     byte: @integer;
     putBH: (*private*)@...
  enter (x,byte)
  do INNER putByteHex; putBH
  #);

putOctal:
  (* prints a octal representation of x (as unsigned word) on
   * this (stream). Similar to
   * (8,x)->putRadix(# do true->zeroPadding;  #)
   * but more efficient.
   *)
  (# width: @integer;
     zeroPadding: @boolean;
     x: @integer;
     format:< (# do INNER #);
     putO:(*private*)@...
  enter x
  do true->zeroPadding; format; INNER putOctal; putO;
  #);

putBinary:
  (* prints a binary representation of x (as unsigned word) on
   * this(stream). Similar to
   * (2,x)->putRadix(# do true->zeroPadding; #)
   * but more efficient.
   *)
  (# width: @integer;
     zeroPadding: @boolean;
     x: @integer;
     format:< (# do INNER #);
     putB: (*private*)@...
  enter x
  do true->zeroPadding; format; INNER putBinary; putB
  #);

putByteBinary:
  (* prints a binary representation of byte 'byte' of x (as unsigned
   * word) on this(stream)
   *)
  (# x: @integer;
     byte: @integer;
     putBB: (*private*)@...
  enter (x, byte)
  do INNER putByteBinary; putBB
  #);

getHex:
  (* reads a hexadecimal number from this(stream) and returns the
   * value in x (as unsigned word). Similar to 16->getRadix but more
   * efficient.
   *)
  (# x: @integer;
     noNumberError:< streamException
       (# peekCh: @char
       enter peekCh
       do 'getHex: the number begins with: "'->msg.append;
          (if peekCh = ascii.nul then 'NUL'->msg.puttext
           else peekCh->msg.put if);
          '".  Not a legal hexadecimal digit'->msg.puttext;
          INNER noNumberError
       #);
     getH: (*private*)...
  do INNER getHex; getH
  exit x
  #);

getOctal:
  (* reads a hexadecimal number from this(stream) and returns the
   * value in x (as unsigned word). Similar to 16->getRadix but more
   * efficient.
   *) 
  (# x: @integer;
    noNumberError:< streamException
       (# peekCh: @char
       enter peekCh
       do 'getHex: the number begins with: "'->msg.append;
          (if peekCh = ascii.nul then 'NUL'->msg.puttext
           else peekCh->msg.put if);
          '".  Not a legal hexadecimal digit'->msg.puttext;
          INNER noNumberError
       #);
     getO: (*private*)...
  do INNER getOctal; getO
  exit x
  #);

getBinary:
  (* reads a binary number from this(stream) and returns the value in
   * x (as unsigned word). Similar to 2->getRadix but more efficient.
   *)
  (# x: @integer;
     noNumberError:< streamException
       (# peekCh: @char
       enter peekCh
       do 'getBinary: the number begins with: "'->msg.append;
          (if peekCh = ascii.nul then 'NUL'->msg.puttext
           else peekCh->msg.put if);
          '".  Not a legal binary digit'->msg.puttext;
          INNER noNumberError
       #);
     getB: (*private*)...
  do INNER getBinary; getB
  exit x
  #);

asNumber:
  (# syntaxError:< streamException
       (# peekCh: @char
       enter peekCh
       do 'asNumber: Syntax error - looking at: "'->msg.append;
          peekCh->msg.put; '"'->msg.put;
          INNER syntaxError
       #);
     baseError:< streamException
       (# base: @integer
       enter base
       do 'asNumber: Error in base - looking at: "'->msg.append;
          base->msg.putInt; '"'->msg.put;
          INNER baseError
       #);
     valueError:< streamException
       (# peekCh: @char
       enter peekCh
       do 'asNumber: Illegal value type - looking at: "'->msg.append;
          peekCh->msg.put; '"'->msg.put;
          INNER valueError
       #);
  do reset;
     INNER asNumber;
     ScanWhiteSpace; (if not eos then peek->syntaxError if)
  #);

asReal: asNumber
  (# r: @real
    ...
  exit r
  #);
asBased: asNumber
  (# i, b: @integer
    ...
  exit (b,i)
  #);
asRadix: asNumber
  (# radix, value: @integer
  enter radix
  ...
  exit value
  #);
asInteger: asNumber
  (# i: @integer
    ...
  exit i
  #)

--- lib: attributes ---

getNumber: keyboard.getNumber
  (# do INNER getNumber #);
getReal: keyboard.getReal
  (# do INNER getReal #);
getBased: keyboard.getBased
  (# do INNER getbased #);
getRadix: keyboard.getRadix
  (# do INNER getRadix #);
getInteger: keyboard.getInteger
  (# do INNER getInteger #);

putReal: screen.putReal
  (# do INNER putReal #);
putBased: screen.putBased
  (# do INNER putBased #);
putRadix: screen.putRadix
  (# do INNER putRadix #);

getHex: keyboard.getHex
  (# do INNER getHex #);
getOctal: keyboard.getOctal
  (# do INNER getOctal #);
getBinary: keyboard.getBinary
  (# do INNER getBinary #);

putHex: screen.putHex
  (# do INNER putHex #);
putByteHex: screen.putByteHex
  (# do INNER putByteHex #);
putOctal:screen.putOctal
  (# do INNER putOctal #);
putBinary: screen.putBinary
  (# do INNER putBinary #);
putByteBinary: screen.putByteBinary
  (# do INNER putByteBinary #);


real2ints:
  (* Extract the high- and low bits of the real r into (i1, i2) *)
  (# r: @real;
     i1, i2: @int32;
  enter r
  ...
  exit (i1, i2)
  #);

ints2real:
  (* Combine the high- and low bits in (i1, i2) to form the real r *)
  (# i1, i2: @int32;
     r: @real;
  enter (i1, i2)
  ...
  exit r
  #)


13.12 Numberio Interface
© 1990-2004 Mjølner Informatics
[Modified: Tuesday October 14th 2003 at 15:53]