I'm developing one at https://git.sr.ht/~remexre/rtf -- it's nowhere near ready for general use, but idiomatic code looks like:
: parse-digit ( u -- u -1 | 0 0 )
{ DUP $30 $3a WITHIN } { $30 - TRUE }
{ DUP $41 $5b WITHIN } { $37 - TRUE }
{ DUP $61 $7b WITHIN } { $57 - TRUE }
{ ." parse-digit 1: " TODO } 3 CASE ;
: add-digit ( u u -- u flag )
." add-digit 0: " TODO
;
: parse-digits-0 ( u addr len -- u flag )
{ OVER B@ -ROT { parse-digit } 2DIP ROT
{ { SWAP base B@ * + } 2DIP 1 /STRING parse-digits-0 } WHEN
} { DROP TRUE } ?COND ;
: parse-digits ( u addr len -- u flag )
{ parse-digits-0 } { DROP FALSE } ?COND ;
: parse-prefix-if-exists ( addr len -- addr len 0|1|2 )
DUP {
OVER B@
{ DUP $2d = } { DROP 1 /STRING 1 }
{ DUP $7e = } { DROP 1 /STRING 2 }
{ DROP 0 } 2 CASE
} { 0 } COND ;
: parse-base-if-exists ( addr len -- addr len base )
DUP {
OVER B@
{ DUP $23 = } { DROP 1 /STRING #10 }
{ DUP $24 = } { DROP 1 /STRING $10 }
{ DUP $25 = } { DROP 1 /STRING %10 }
{ DUP $5e = } { DROP 1 /STRING ^10 }
{ DROP 2DUP $3a SCAN DUP } {
1 /STRING { NIP - 1- 10 BASE B! 0 -ROT parse-digits } 2KEEP
ROT { ROT } {
." parse-base-if-exists 1: " TODO
} COND
\ ." parse-base-if-exists 2: " TODO
}
{ 2DROP base B@ } 5 CASE
} { base B@ } COND ;
: with-saved-base ( x*i { x*i -- x*j } -- x*j )
base B@ SWAP DIP base B! ;
: apply-prefix ( u 0|1|2 -- n )
{ } { NEGATE } { INVERT } 3 SWITCH ;
: parse-number ( addr len -- u -1 | 0 0 )
{ parse-base-if-exists base B! parse-prefix-if-exists
{ 0 -ROT parse-digits } DIP
SWAP { apply-prefix TRUE } { DROP FALSE } COND
} with-saved-base ;
(This being an incomplete rewrite of the bootstrapped text interpreter's number parser; as I said, nowhere near ready.)