Miser's Case - A general purpose Forth Case statement

Revision  2014-03-08

This Case statement offers the following features:

It is called Miser's Case because it immodestly claims to 'do everything yet cost nothing'.


Implementation

The following high-level implementation is intended as a guide only. Two custom
implementations
are provided. [See also CFS extensions for a portable version.]


\ Miser's Case
\
\ A general purpose Forth case statement.
\
\ Revision  2014-03-08
\
\ -------------------------------------------------------------
\ History
\ - RANGE changed to use BETWEEN.
\   Add CONTINUE for C-style switching.
\ - Examples revised. Minor text changes.
\ - Add THENS END-CASE.
\ - EQUAL RANGE re-coded for optimizers.
\ - Text and example re-worded to emphasize use of
\   COND THENS while deprecating Forth-94 CASE.
\ - Add custom implementations
\ -------------------------------------------------------------
\
\ Sample implementation only
\
\ The high-level implementation makes assumptions about the
\ control flow stack which may not be applicable to your forth:
\ control flow is on the data stack; control flow items are 1,
\ 2 or 3 cells wide; the number 0 (single cell) is used as a
\ sentinel.
\
\ For all but the best optimizing Forth compilers, the run-time
\ should be replaced with machine-code primitives for maximum
\ performance.
\
\ COND THENS is per Wil Baden and provides the mechanism for
\ resolving nested conditionals.
\
\ Tested on SwiftForth, VFX, Win32Forth, Gforth and others.
\
\ This code is public domain. Use at your own risk.
\
\ Keywords:
\
\   COND OF IF ELSE THENS CONTINUE EQUAL RANGE WHEN
\
\ Syntax:
\
\   COND ( x1)
\      x2           OF         ... ELSE
\      COND <tests> WHEN       ... ELSE
\      <test>       IF ( DROP) ... ELSE
\      ( x1) ( DROP) ... ( default )
\   THENS
\
\ COND ... THENS is analogous to CASE ... ENDCASE with
\ the exception that THENS does not automatically DROP x1.
\
\ OF performs the same function as the Forth-94 word but
\ in addition may be used with ELSE or THEN. ( x1) x2 OF
\ is the short-form of ( x1) COND x2 EQUAL WHEN.
\
\ COND <tests> WHEN where <tests> may consist of one or
\ more of the following:
\
\    x2    EQUAL  ( test if x1 equals x2 )
\    x2 x3 RANGE  ( test if x1 is in the range x2..x3 )
\
\ Miser's COND ... WHEN generates compact efficient code
\ comparable with other language compilers.
\
\ <test> IF where <test> can be any code which leaves x1
\ and a flag for IF. IF ... ELSE is for expansion allowing
\ user-defined tests including those where x1 is not
\ necessarily an integer.
\
\ CONTINUE redirects program flow from previously matched
\ tests that would otherwise pass to THENS. It provides a
\ 'fall-through' capability akin to C's switch statement.
\ CONTINUE may be placed anywhere within:
\
\   OF ... ELSE
\   WHEN ... ELSE
\   IF ( DROP) ... ELSE
\

0 constant COND  immediate

: THENS
  begin  ?dup while  postpone then  repeat ; immediate

cr .( Are you using SwiftForth or VFX? Y/N )
key dup emit cr dup char Y = swap char y = or
[if]

: WHEN
  postpone else  >r  postpone thens  r>  postpone drop ;
  immediate

: CONTINUE
  >r  postpone thens  postpone cond  r> ; immediate

[else]

cr .( Are you using gForth Y/N )
key dup emit cr dup char Y = swap char y = or
[if]

: WHEN
  postpone else  >r >r >r  thens  r> r> r>  postpone drop ;
  immediate

: CONTINUE
  >r >r >r  postpone thens  postpone cond  r> r> r> ;
immediate

[else]

: WHEN
  postpone else  2>r  postpone thens  2r>  postpone drop ;
  immediate

: CONTINUE
  2>r  postpone thens  postpone cond  2r> ; immediate

[then]
[then]

: EQUAL
  postpone over  postpone <>  postpone if ; immediate

\ RANGE is based on  : BETWEEN OVER - -ROT - U< 0= ;
\ Values may be signed or unsigned.
: (range)
  2 pick -rot over - -rot - u< ;

: RANGE
  postpone (range)  postpone if ; immediate

: OF  postpone over  postpone =  postpone if
  postpone drop ; immediate

\ Forth-94 compatibility words
: CASE  postpone cond ; immediate

: ENDOF  postpone else ; immediate

: ENDCASE  postpone drop  postpone thens ; immediate

Example

\ Using VFX 4.02 for Windows the following code compiles
\ to 69 instructions using Miser's Case, compared with 131
\ instructions for an equivalent FORTH-94 based CASE.

hex

: TEST1 ( n )  space
  cond
    cond
          00 1F range
          7F    equal  when  ." Control char "       else
    cond
          20 2F range
          3A 40 range
          5B 60 range
          7B 7E range  when  ." Punctuation "        else
    cond  30 39 range  when  ." Digit "              else
    cond  41 5A range  when  ." Upper case letter "  else
    cond  61 7A range  when  ." Lower case letter "  else
      drop ." Not a character "
  thens ;

decimal

cr cr .( Running TEST...)

cr  char a  .(   ) dup emit  test1
cr  char ,  .(   ) dup emit  test1
cr  char 8  .(   ) dup emit  test1
cr  char ?  .(   ) dup emit  test1
cr  char K  .(   ) dup emit  test1
cr  0              dup 3 .r  test1
cr  127            dup 3 .r  test1
cr  128            dup 3 .r  test1

\ end

Custom implementations

Note: For systems which do not accept the sequence OF ... ELSE ... THEN it will be
necessary to redefine OF. In such cases it may also be necessary to redefine CASE ENDOF
ENDCASE
as previously described.

1. 80x86 32-bit native code

\ Miser's Case for SwiftForth

ONLY FORTH ALSO DEFINITIONS DECIMAL

AKA CASE COND  IMMEDIATE

: THENS
  BEGIN  ?DUP WHILE  POSTPONE THEN  REPEAT  -BAL ; IMMEDIATE

ICODE (EQU)
  0 [EBP] EBX CMP  0= IF
    4 [EBP] EBX MOV  8 # EBP ADD  HERE $400 + JMP
  THEN  RET
END-CODE

ICODE (RNG)
  0 [EBP] EBX SUB  4 [EBP] EDX MOV  0 [EBP] EDX SUB
  EDX EBX CMP  U>= IF
    8 [EBP] EBX MOV  12 # EBP ADD  HERE $400 + JMP
  THEN  RET
END-CODE

: EQUAL  POSTPONE (EQU)  HERE +BAL  POSTPONE DROP ; IMMEDIATE

: RANGE  POSTPONE (RNG)  HERE +BAL  POSTPONE 2DROP ; IMMEDIATE

: WHEN
  POSTPONE ELSE  >R  POSTPONE THENS  R> ; IMMEDIATE

: CONTINUE
  >R  POSTPONE THENS  POSTPONE COND  R> ; IMMEDIATE

2. 8086 16-bit DTC

\ Miser's Case for DX-Forth

code (of)
  bx pop  ax pop  bx ax cmp  1 $ jz  ax push
  0 [si] si mov  next  1 $:  2 # si add  next
end-code

code (equ)
  bx pop  ax pop  ax bx cmp  1 $ jz  ax push
  2 # si add  next  1 $:  0 [si] si mov  next
end-code

code (rng)
  bx pop  dx pop  ax pop  ax cx mov  dx cx sub
  dx bx sub  bx cx cmp  1 $ jna  ax push
  2 # si add  next  1 $:  0 [si] si mov  next
end-code

0 constant COND  immediate

: THENS
  begin  ?dup while  postpone then  repeat ;
  immediate

: OF
  postpone (of) >mark ; immediate

: EQUAL
  postpone (equ) >mark ; immediate

: RANGE
  postpone (rng) >mark ; immediate

: WHEN
  postpone else  >r  postpone thens  r> ; immediate

: CONTINUE
  >r  postpone thens  postpone cond  r> ; immediate

Miser's Case extension for DX-Forth is provided in the distribution.

Top    Home    Forth


em.gif (457 bytes)


web stats

Page updated: 2014-03-08