问题
Is there something like input
in Basic or scanf("%d")
in C in Forth?
Probably it will be something like this:
200 buffer: buf
: input ( -- n ) buf 200 accept
some-magic-filter
buf swap evaluate ;
The problem in the above code, is how to define a filter that will pass only numbers, but not any words, definitions, etc?
回答1:
The standard specifies only a low level >NUMBER word to interpret integer numbers.
OTOH using EVALUATE
to convert strings into numbers is a quick and dirty way. Either use it without checks (in the case of trusted input) or do not use it at all. Trying to filter the string before EVALUATE
is a bad idea: it has cost of >NUMBER
word itself and low reusing factor.
NB: neither >NUMBER
nor EVALUATE
detects numeric overflow.
In any case, your word to input a single-cell integer can be defined something like:
: accept-number ( -- n )
PAD DUP 80 ACCEPT ( addr u ) StoN ( n )
;
In the case of trusted input you can define StoN
like
: StoN ( addr u -- x )
STATE @ ABORT" This naive StoN should not be used in compilation state"
DEPTH 2- >R
EVALUATE
DEPTH 1- R> <> IF -24 THROW THEN
\ check depth to accept the single-cell numbers only
;
Otherwise (in the case of untrusted input) you have two choices: to rely on the specific words of a particular Forth system or to use some (perhaps your own) library.
I use the following lexicon to define StoN
:
\ ---
\ The words from Substring Matching library
\ (where length is counted in address units)
: MATCH-HEAD ( a u a-key u-key -- a-right u-right true | a u false )
2 PICK OVER U< IF 2DROP FALSE EXIT THEN
DUP >R
3 PICK R@ COMPARE IF RDROP FALSE EXIT THEN
SWAP R@ + SWAP R> - TRUE
;
\ ---
\ The words from Literals interpreting library
\ (where prefix 'I-' is shortcut for Interpret)
: I-DLIT ( a u -- x x true | a u false )
2DUP S" -" MATCH-HEAD >R
DUP 0= IF NIP RDROP EXIT THEN
0 0 2SWAP >NUMBER NIP IF RDROP 2DROP FALSE EXIT THEN
R> IF DNEGATE THEN 2SWAP 2DROP TRUE
;
: I-LIT ( a u -- x true | a u false )
I-DLIT IF D>S TRUE EXIT THEN FALSE
;
After that StoN
can be defined as:
: StoN ( a u -- x ) I-LIT IF EXIT THEN -24 THROW ;
The mentioned libraries can be found at GitHub:
- Substring matching functions library
- Resolvers example (for various lexemes)
回答2:
Rosetta Code suggests this code snippet, working with GForth 0.6.2, to determine if an input string is numeric:
: is-numeric ( addr len -- )
2dup snumber? ?dup if
0< if
-rot type ." as integer = " .
else
2swap type ." as double = " <# #s #> type
then
else 2dup >float if
type ." as float = " f.
else
type ." isn't numeric in base " base @ dec.
then then ;
来源:https://stackoverflow.com/questions/51704398/how-to-enter-numbers-in-forth