whartung wrote:
This is the text of the comment from the F83 source code:
Quote:
Starting with the unsigned double number ud1 and the string
at adr1, convert the string to a number in the current base.
Leave result and address of unconvertable digit on stack.
And here's the routine they do:
Code:
: CONVERT (S +d1 adr1 -- +d2 adr2 )
BEGIN 1+ DUP >R C@ BASE @ DIGIT
WHILE SWAP BASE @ UM* DROP ROT BASE @ UM* D+
DOUBLE? IF 1 DPL +! THEN R>
REPEAT DROP R> ;
There's no mention of "+2's" or anything here.
That is how Fleet Forth's
CONVERT behaves. The wording of the Forth-83 Standard had me wondering if this was one more place in Fleet Forth that would not quite conform to the standard ( not all the 'user variables' from the Forth-83 standard are user variables in Fleet Forth). It looks like F83's
DIGIT returns two items regardless of whether the character is a valid digit. Since
DIGIT isn't in the Forth-83 Standard, I wrote Fleet Forth's
DIGIT to return the same parameters as the one from "All About Forth".
Code:
CODE DIGIT ( C B -- 0 ) // NOT A DIGIT
( C B -- N TF ) // VALID DIGIT
Fleet Forth's
CONVERT is coded a little differently to accommodate that difference and to be more memory efficient.
Code:
SCR# 8B
// CONVERT
HEX
: CONVERT ( D1 ADR1 -- D2 ADR2 )
1+
BEGIN
COUNT BASE @ DIGIT
WHILE
2SWAP BASE @ *
SWAP BASE @ UM* D+
ROT
DPL @ 0< 1+ DPL +!
REPEAT
1- ;
By the way,
UM* DROP produces the same result as
* .
Quote:
But do notice that the first thing CONVERT does is 1+.
For example:
Code:
create nn 4 c, 49 c, 50 c, 51 c, 52 c, ok
nn count type 1234 ok
nn number? .s 1234 0 0 ok
Here, we have 1234 as a counted string. TYPE shows it ok, NUMBER? converts it ok (for some reason F83 has NUMBER, but it's not in the FORTH vocabulary -- but NUMBER? is, and NUMBER is just DEFERed to that).
However, CONVERT does not take a counted string, it wants raw text. It wants a double, and the address of the text. So:
Code:
0 0 nn 1+ convert .s 234 0 26391 ok
You can see it skipped the first character, and just converted 234.
Here is how it's used:
Code:
: (NUMBER?) (S adr -- d flag )
0 0 ROT DUP 1+ C@ ASCII - = DUP >R - -1 DPL !
BEGIN CONVERT DUP C@ ASCII , ASCII / BETWEEN
WHILE 0 DPL !
REPEAT -ROT R> IF DNEGATE THEN ROT C@ BL = ;
The reason CONVERT is skipping the first byte is for multiple reasons. Initially, it's because it's expecting a counted string -- and it just ignores the count by skipping over it.
Here, the first line is basically staging the initial double (0 0), and then checks for a sign character. It compares to the '-', if it sees it, then it bumps the address up 1. (= returns TRUE, which is -1, so it subtracts it -- no, this is not intuitive).
However, it's not just skipping the count. It just does that at the beginning. It also consumes any , - . or / characters within the number. So, it's skipping over those as well.
So, if you key in: 123-456-789, (NUMBER?) will call CONVERT 3 times, once just before the 1, (which is normally a count byte), then at each of the dashes. CONVERT implicitly skips those.
Code:
123
123.456
123.456/789
1,234
Are all valid numbers in Forth. When you add those characters, you get double numbers.
Yes,
CONVERT's behavior allows for some interesting tricks. For example if there is a need to convert a time string with the following format: " hh:mm:ss " to the hours minutes and seconds on the stack, this short routine will do it.
Code:
: TIME> ( ADR -- H M S )
0 0 ROT CONVERT
0 SWAP CONVERT
0 SWAP CONVERT
2DROP ;
And here is a sample session.
Code:
OK
" 2:55:00 " OK
DUP COUNT CR TYPE
2:55:00 OK
TIME> OK
.S 2 55 0 OK
CONSOLE
This:
Code:
: (NUMBER?) (S adr -- d flag )
0 0 ROT DUP 1+ C@ ASCII - = DUP >R - -1 DPL !
BEGIN CONVERT DUP C@ ASCII , ASCII / BETWEEN
WHILE 0 DPL !
REPEAT -ROT R> IF DNEGATE THEN ROT C@ BL = ;
Is not how I defined Fleet Forth's number word,
-NUMBER .
This version also accepts the following as valid numbers:
Code:
.
,
-
/
..
...
..///,,,---
As well as any variation of those four punctuation marks.
This is how
-NUMBER is defined in Fleet Forth.
Code:
SCR# 8D
// -NUMBER
: -NUMBER ( ADR -- D FLAG )
DPL ON 0 0 ROT
DUP 1+ C@ ASCII - =
DUP>R - DUP>R
BEGIN
CONVERT DUP C@ VALID?
WHILE
DUP 1+ C@ VALID? 0=
WHILE
DPL OFF
REPEAT
THEN
DUP C@ BL <> SWAP
2- DPL @ 0< - R> = OR
R> 0EXIT >R DNEGATE R> ;
-NUMBER returns a double number and a true flag if the conversion fails.
It returns a double number and a false flag if the conversion is successful.
For a successful conversion, the number string must contain a valid digit in the current number BASE and there can not be a run of two or more non digit valid characters. In other words, if
, - . and
/ are valid punctuation for a number, these are valid numbers:
Code:
1.2
1.2.
1.2.3.
2,000,000
.1.2.3.456
123-456.12
And these are not:
Code:
1..2
3..4.5
...1
5.....
76.,
231-/..3
VALID? is a
DEFERred word, the punctuation which is considered valid punctuation for a number can be changed.