Discussion:
Parsing timestamps?
(too old to reply)
dxf
2024-10-06 07:51:31 UTC
Permalink
Is there an easier way of doing this? End goal is a double number representing centi-secs.


empty decimal

: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;

: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;

: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;

s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
mhx
2024-10-06 08:59:23 UTC
Permalink
Post by dxf
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
Why don't you use the fact that >NUMBER returns the given
string starting with the first unconverted character?
SPLIT should be redundant.

-marcel
dxf
2024-10-06 12:58:55 UTC
Permalink
Post by mhx
Is there an easier way of doing this?  End goal is a double number
representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 )  >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u )  0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
  2 0 do  [char] : split  2swap  dup if 1 /string then  loop
  2 0 do  dup 0= if 2rot 2rot then  loop ;
: .T  2swap 2rot  cr  >int . ." hr "  >int . ." min " >int . ." sec " ;
s" 1:2:3"    /t .t
s" 02:03"    /t .t
s" 03"       /t .t
s" 23:59:59" /t .t
s" 0:00:03"  /t .t
Why don't you use the fact that >NUMBER returns the given
string starting with the first unconverted character?
SPLIT should be redundant.
Good idea!

: /T ( a u -- hour min sec )
3 0 do
0 0 2swap >number rot drop ( u ... a u)
dup if over c@ [char] : = 1 and /string then
loop 2drop
2 0 do dup 0= if rot rot then loop ;

: .T cr . ." sec " . ." min " . ." hr " ;

s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
dxf
2024-10-06 15:34:19 UTC
Permalink
Post by dxf
Post by mhx
Is there an easier way of doing this?  End goal is a double number
representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 )  >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u )  0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
  2 0 do  [char] : split  2swap  dup if 1 /string then  loop
  2 0 do  dup 0= if 2rot 2rot then  loop ;
: .T  2swap 2rot  cr  >int . ." hr "  >int . ." min " >int . ." sec " ;
s" 1:2:3"    /t .t
s" 02:03"    /t .t
s" 03"       /t .t
s" 23:59:59" /t .t
s" 0:00:03"  /t .t
Why don't you use the fact that >NUMBER returns the given
string starting with the first unconverted character?
SPLIT should be redundant.
Good idea!
: /T ( a u -- hour min sec )
3 0 do
0 0 2swap >number rot drop ( u ... a u)
loop 2drop
2 0 do dup 0= if rot rot then loop ;
: .T cr . ." sec " . ." min " . ." hr " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
Bug fix:

variable cnt

: @SEC ( a u -- ud ) \ HH:MM:SS or free-form
0 cnt ! 3 0 do
0 0 2swap >number rot drop ( u ... a u)
dup if 1 /string 1 cnt +! then
loop 2drop cnt @ 1+ 0 do rot loop ( fixup)
; \ swap secs/min * + swap secs/hour um* rot 0 d+ ;

: .3 ( h m s -- ) cr rot . swap . . ;

s" 00:00:06" @sec .3
s" 00:06:00" @sec .3
s" 06:00:00" @sec .3
cr
s" 06" @sec .3
s" 06:00" @sec .3
s" 06:00:00" @sec .3
dxf
2024-10-07 12:53:59 UTC
Permalink
Post by dxf
...
variable cnt
0 cnt ! 3 0 do
0 0 2swap >number rot drop ( u ... a u)
dup if 1 /string 1 cnt +! then
; \ swap secs/min * + swap secs/hour um* rot 0 d+ ;
: .3 ( h m s -- ) cr rot . swap . . ;
cr
That's not foolproof either! My conclusion is the only reliable way to do it
is as per the 'Reverse SCAN SPLIT' thread.
Ruvim
2024-10-06 10:48:14 UTC
Permalink
Post by dxf
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
I would use `split-string` factor as:

: /t ( sd.time -- sd.hour sd.min sd.sec )
s" :" split-string
s" :" split-string
;

\ Where

: split-string
( sd.text sd.separator -- sd.left sd.right | sd.text 0 0 )
dup >r 3 pick >r ( R: u.[sd.separator][1] addr.[st.text][2] )
search 0= if 2rdrop 0 0 exit then ( addr u )
over r@ - r> swap 2swap r> /string
;



--
Ruvim
dxf
2024-10-06 11:59:36 UTC
Permalink
Is there an easier way of doing this?  End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 )  >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u )  0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
   2 0 do  [char] : split  2swap  dup if 1 /string then  loop
   2 0 do  dup 0= if 2rot 2rot then  loop ;
: .T  2swap 2rot  cr  >int . ." hr "  >int . ." min " >int . ." sec " ;
s" 1:2:3"    /t .t
s" 02:03"    /t .t
s" 03"       /t .t
s" 23:59:59" /t .t
s" 0:00:03"  /t .t
  : /t ( sd.time -- sd.hour sd.min sd.sec )
    s" :" split-string
    s" :" split-string
  ;
  \ Where
  : split-string
    ( sd.text sd.separator -- sd.left sd.right | sd.text 0 0 )
    dup >r  3 pick >r  ( R: u.[sd.separator][1] addr.[st.text][2] )
    search 0= if 2rdrop 0 0 exit then ( addr u )
  ;
It fails with s" 03". The test case may be unreasonable so I tried
s" :03" however it also fails. The complication is most tools scan
from the beginning whereas we would like to scan from the end.
Ruvim
2024-10-06 13:22:53 UTC
Permalink
Post by dxf
Is there an easier way of doing this?  End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 )  >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u )  0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
   2 0 do  [char] : split  2swap  dup if 1 /string then  loop
   2 0 do  dup 0= if 2rot 2rot then  loop ;
: .T  2swap 2rot  cr  >int . ." hr "  >int . ." min " >int . ." sec " ;
s" 1:2:3"    /t .t
s" 02:03"    /t .t
s" 03"       /t .t
s" 23:59:59" /t .t
s" 0:00:03"  /t .t
  : /t ( sd.time -- sd.hour sd.min sd.sec )
    s" :" split-string
    s" :" split-string
  ;
  \ Where
  : split-string
    ( sd.text sd.separator -- sd.left sd.right | sd.text 0 0 )
    dup >r  3 pick >r  ( R: u.[sd.separator][1] addr.[st.text][2] )
    search 0= if 2rdrop 0 0 exit then ( addr u )
  ;
It fails with s" 03". The test case may be unreasonable so I tried
s" :03" however it also fails. The complication is most tools scan
from the beginning whereas we would like to scan from the end.
You did not provide output for test cases.

I expect that "03" is equivalent to "03:00:00", which means 3 hours, 0
minutes, 0 seconds.
And ":03" is equivalent to "00:03:00", which means 0 hours, 3 minutes, 0
seconds.

My above implementation for `/t` produces:

s" 1:2:3" /t .t \ "1 hr 2 min 3 sec"
s" 02:03" /t .t \ "2 hr 3 min 0 sec"
s" 03" /t .t \ "3 hr 0 min 0 sec"
s" :03" /t .t \ "0 hr 3 min 0 sec"


What is wrong?

Maybe your `>int` works incorrectly with an empty string?


For testing I use:

: >int ( sd.number -- u ) 0. 2swap >number 2drop drop ;
\ the empty string produces 0


--
Ruvim
Ruvim
2024-10-06 13:34:27 UTC
Permalink
Post by Ruvim
Is there an easier way of doing this?  End goal is a double number
representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 )  >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u )  0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
    2 0 do  [char] : split  2swap  dup if 1 /string then  loop
    2 0 do  dup 0= if 2rot 2rot then  loop ;
: .T  2swap 2rot  cr  >int . ." hr "  >int . ." min " >int . ." sec " ;
s" 1:2:3"    /t .t
s" 02:03"    /t .t
s" 03"       /t .t
s" 23:59:59" /t .t
s" 0:00:03"  /t .t
   : /t ( sd.time -- sd.hour sd.min sd.sec )
     s" :" split-string
     s" :" split-string
   ;
   \ Where
   : split-string
     ( sd.text sd.separator -- sd.left sd.right | sd.text 0 0 )
     dup >r  3 pick >r  ( R: u.[sd.separator][1] addr.[st.text][2] )
     search 0= if 2rdrop 0 0 exit then ( addr u )
   ;
It fails with s" 03".  The test case may be unreasonable so I tried
s" :03"  however it also fails.  The complication is most tools scan
from the beginning whereas we would like to scan from the end.
You did not provide output for test cases.
I expect that "03" is equivalent to "03:00:00", which means 3 hours, 0
minutes, 0 seconds.
And ":03" is equivalent to "00:03:00", which means 0 hours, 3 minutes, 0
seconds.
  s" 1:2:3"   /t .t   \ "1 hr 2 min 3 sec"
  s" 02:03"   /t .t   \ "2 hr 3 min 0 sec"
  s" 03"      /t .t   \ "3 hr 0 min 0 sec"
  s" :03"     /t .t   \ "0 hr 3 min 0 sec"
What is wrong?
Maybe your `>int` works incorrectly with an empty string?
  : >int ( sd.number -- u ) 0. 2swap >number 2drop drop ;
  \ the empty string produces 0
Oh, I missed your definition for ">INT", because you use ">int" (sic
lower case) in your definition for ".T"


Then, it's unclear what fails on your side.


--
Ruvim
Ruvim
2024-10-06 16:15:42 UTC
Permalink
Post by Ruvim
Is there an easier way of doing this?  End goal is a double number
representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 )  >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u )  0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
    2 0 do  [char] : split  2swap  dup if 1 /string then  loop
    2 0 do  dup 0= if 2rot 2rot then  loop ;
: .T  2swap 2rot  cr  >int . ." hr "  >int . ." min " >int . ." sec " ;
s" 1:2:3"    /t .t
s" 02:03"    /t .t
s" 03"       /t .t
s" 23:59:59" /t .t
s" 0:00:03"  /t .t
   : /t ( sd.time -- sd.hour sd.min sd.sec )
     s" :" split-string
     s" :" split-string
   ;
   \ Where
   : split-string
     ( sd.text sd.separator -- sd.left sd.right | sd.text 0 0 )
     dup >r  3 pick >r  ( R: u.[sd.separator][1] addr.[st.text][2] )
     search 0= if 2rdrop 0 0 exit then ( addr u )
   ;
It fails with s" 03".  The test case may be unreasonable so I tried
s" :03"  however it also fails.  The complication is most tools scan
from the beginning whereas we would like to scan from the end.
You did not provide output for test cases.
I expect that "03" is equivalent to "03:00:00", which means 3 hours, 0
minutes, 0 seconds.
And ":03" is equivalent to "00:03:00", which means 0 hours, 3 minutes, 0
seconds.
  s" 1:2:3"   /t .t   \ "1 hr 2 min 3 sec"
  s" 02:03"   /t .t   \ "2 hr 3 min 0 sec"
  s" 03"      /t .t   \ "3 hr 0 min 0 sec"
  s" :03"     /t .t   \ "0 hr 3 min 0 sec"
What is wrong?
Well, I see what you meant.

: /t ( sd.time -- sd.hour sd.min sd.sec )
s" :" split-string dup 0= if 2swap 0. 2swap exit then
s" :" split-string dup 0= if 2rot 2rot then
;

s" 02:03" /t .t \ "0 hr 2 min 3 sec"
s" 03" /t .t \ "0 hr 0 min 3 sec"
s" :03" /t .t \ "0 hr 0 min 3 sec"



Probably, a better interface would be:

: parse-time ( sd.time -- u.sec u.min u.hour )
s" :" split-string-last parse-uint -rot
s" :" split-string-last parse-uint -rot
parse-uint
;


--
Ruvim
FFmike
2024-10-06 13:08:56 UTC
Permalink
In FlashForth I usually do this.

FlashForth allows only "." as number punctuation.

: /t ( addr len -- )
'source 2@ >r >r >in @ >r
'source 2! 0 >in !
3 for
[char] : word number? 1 <> if drop 0 then
next
r> >in ! r> r> 'source 2!
;

s" 12:33:44" /t . . .
44 33 12 ok
FFmike
2024-10-06 13:33:53 UTC
Permalink
Post by FFmike
FlashForth allows only "." as number punctuation.
This was nonsense, NUMBER? never sees the punctuation anyway.
But I added error handling in case someone puts a double number
punctuation in the string.

: /t ( addr len -- )
'source 2@ >r >r >in @ >r
'source 2! 0 >in !
3 for
[char] : word number?
dup 1 = if
drop
else
2 = if drop else drop 0 then
then
next
r> >in ! r> r> 'source 2!
;
dxf
2024-10-07 02:10:56 UTC
Permalink
Post by FFmike
Post by FFmike
FlashForth allows only "." as number punctuation.
This was nonsense, NUMBER? never sees the punctuation anyway.
But I added error handling in case someone puts a double number
punctuation in the string.
: /t ( addr len -- )
 'source 2! 0 >in !
 3 for
   [char] : word number?
   dup 1 = if
     drop
   else
     2 = if drop else drop 0 then
   then
 next
 r> >in ! r> r> 'source 2!
;
The problem is handling the partial forms.

s" 6:0:0" /t
s" 6:0" /t
s" 6" /t
FFmike
2024-10-07 03:29:34 UTC
Permalink
Post by dxf
The problem is handling the partial forms.
s" 6:0:0" /t
s" 6:0" /t
s" 6" /t
Not a problem on my system.

s" 12" /t . . . 0 0 12 ok<$,ram>
s" 12:13" /t . . . 0 13 12 ok<$,ram>
s" 12:13:0" /t . . . 0 13 12 ok<$,ram>

I made up EXECUTE-PARSING so now it looks like this.

: execute-parsing ( c-addr u xt -- )
r> execute
r> >in ! r> r> 'source 2!
;

: (/t) ( addr len -- hrs mins secs)
3 for
[char] : word number?
dup 1 = if
drop
else
2 = if drop else drop 0 then
then
next
;

: /t ( addr len -- hrs mins secs )
['] (/t) execute-parsing
;
dxf
2024-10-07 03:58:28 UTC
Permalink
Post by FFmike
Post by dxf
The problem is handling the partial forms.
s" 6:0:0" /t
s" 6:0" /t
s" 6" /t
Not a problem on my system.
s" 12" /t . . . 0 0 12  ok<$,ram>
s" 12:13" /t . . . 0 13 12  ok<$,ram>
s" 12:13:0" /t . . .  0 13 12  ok<$,ram>
That treats '12' as hours and '12:13' as hours/min when it should be
secs and min/secs respectively.
FFmike
2024-10-07 05:23:16 UTC
Permalink
Post by dxf
That treats '12' as hours and '12:13' as hours/min when it should be
secs and min/secs respectively.
I did not see that. It can be fixed by comparing source and >in. Maybe
not elegant, but it works.

variable cnt
: (/t) ( addr len -- hrs mins secs)
1 cnt !
3 for
[char] : word number?
dup 1 = if
drop 'source 2+ @ >in @ <> if 1 cnt +! then
else
2 = if 2drop else drop then 0
then
next
cnt @ for rot next ( fixup)
;

: /t ( addr len -- hrs mins secs )
['] (/t) execute-parsing
;
: t. rot . swap . . ;

s" 30" /t t. 0 0 30 ok<$,ram>
s" 29:30" /t t. 0 29 30 ok<$,ram>
s" 1:29:30" /t t. 1 29 30 ok<$,ram>
Anthony Howe
2024-10-06 15:35:24 UTC
Permalink
Post by dxf
Is there an easier way of doing this? End goal is a double number representing centi-secs.
Isn't there an ISO 8601 parsing package?
--
Anthony C Howe
***@snert.com BarricadeMX & Milters
http://nanozen.snert.com/ http://software.snert.com/
dxf
2024-10-07 01:54:33 UTC
Permalink
Post by Anthony Howe
Is there an easier way of doing this?  End goal is a double number representing centi-secs.
Isn't there an ISO 8601 parsing package?
Possibly. In forth there's also the goal of efficiency and fun which
requires doing it oneself.
a***@spenarnc.xs4all.nl
2024-10-07 11:00:10 UTC
Permalink
Post by dxf
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
After ca. 50 years I have completed the $@ $! $+! $C+ $/ with
$\ . Now I can do this

"12:03:43" &: $\ TYPE &: $\ TYPE &: $\ TYPE
43 03 12 OK

"12:03:43" &: $/ TYPE &: $/ TYPE &: $/ TYPE
12 03 43 OK

Insert
"hr" TYPE
as required.

I can't believe the long posts this sparks.

Groetjes Albert
--
Temu exploits Christians: (Disclaimer, only 10 apostles)
Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
And Gifts For Friends Family And Colleagues.
dxf
2024-10-07 11:30:52 UTC
Permalink
Post by a***@spenarnc.xs4all.nl
Post by dxf
Is there an easier way of doing this? End goal is a double number
representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
$\ . Now I can do this
"12:03:43" &: $\ TYPE &: $\ TYPE &: $\ TYPE
43 03 12 OK
"12:03:43" &: $/ TYPE &: $/ TYPE &: $/ TYPE
12 03 43 OK
Insert
"hr" TYPE
as required.
I can't believe the long posts this sparks.
The HH:MM:SS format is easy but how to deal with the variants shown above?
They occur in the real world.
sjack
2024-10-07 16:20:29 UTC
Permalink
Post by dxf
The HH:MM:SS format is easy but how to deal with the variants shown above?
They occur in the real world.
Toad code:
fload job
: xx. 0 <# bl hold # # #> type ;
: tab3. tab rot xx. swap xx. xx. ;

-- &num ( g -- s )
-- Convert g-string to numeric string address
: &num drop 1- ;
-- Note g-string is ANS string ( addr u )

-- ts_elms ( "[hh:][mm:]ss<bl>" -- 0 0 ss | 0 mm ss | hh mm ss )
-- Parse timestamp elements: hh=hours mm=minutes ss=seconds
-- Input hh: element and hh:mm: combination elements may be left out
-- if zero(s).
: ts_elms
bl word here count
o+s do i c@ asc : = if bl i c! then loop
0 0 0 here count
begin
bl split dup 0> while &num number drop
5 roll drop -rot
repeat 4drop
;


ts_elms 25
i. tab3. --> 00 00 25
i. ts_elms 25 tab3. --> 00 00 25
i. ts_elms 10:25 tab3. --> 00 10 25
i. ts_elms 2:10:25 tab3. --> 02 10 25
OK
dxf
2024-10-08 03:07:31 UTC
Permalink
Post by sjack
Post by dxf
The HH:MM:SS format is easy but how to deal with the variants shown above?
They occur in the real world.
fload job
: xx. 0 <# bl hold # # #> type ;
: tab3. tab rot xx. swap xx. xx. ;
-- &num ( g -- s )
-- Convert g-string to numeric string address
: &num drop 1- ;
-- Note g-string is ANS string ( addr u )
-- ts_elms ( "[hh:][mm:]ss<bl>" -- 0 0 ss | 0 mm ss | hh mm ss )
-- Parse timestamp elements: hh=hours mm=minutes ss=seconds
-- Input hh: element and hh:mm: combination elements may be left out
-- if zero(s).
: ts_elms
bl word here count
0 0 0 here count
begin
bl split dup 0> while &num number drop
5 roll drop -rot
repeat 4drop
;
...
Not bad. Here's a translation. Hopefully it's equivalent (?)

: split ( a u c -- a2 u2 a3 u3 )
Post by sjack
r 2dup r> scan 2swap 2 pick - ;
: number ( a u -- u ) 0 0 2swap >number 2drop ;

: xx. ( u -- ) 0 <# bl hold # # #> type ;
: tab3. ( h m s -- ) 3 spaces ( tab) rot xx. swap xx. xx. ;

: ts_elms ( a u -- h m s )
2>r 0 0 0 2r> begin
[char] : skip [char] : split dup 0> while
number drop 5 roll drop -rot
repeat 2drop 2drop ;

s" 25" ts_elms tab3. 00 00 25 ok
s" 10:25" ts_elms tab3. 00 10 25 ok
s" 2:10:25" ts_elms tab3. 02 10 25 ok
Ahmed
2024-10-08 06:08:17 UTC
Permalink
Post by dxf
Not bad. Here's a translation. Hopefully it's equivalent (?)
: split ( a u c -- a2 u2 a3 u3 )
Post by dxf
r 2dup r> scan 2swap 2 pick - ;
: number ( a u -- u ) 0 0 2swap >number 2drop ;
: xx. ( u -- ) 0 <# bl hold # # #> type ;
: tab3. ( h m s -- ) 3 spaces ( tab) rot xx. swap xx. xx. ;
: ts_elms ( a u -- h m s )
2>r 0 0 0 2r> begin
[char] : skip [char] : split dup 0> while
number drop 5 roll drop -rot
repeat 2drop 2drop ;
s" 25" ts_elms tab3. 00 00 25 ok
s" 10:25" ts_elms tab3. 00 10 25 ok
s" 2:10:25" ts_elms tab3. 02 10 25 ok
I know you don't care about this case, but:
s" 1:1:" ts_elms tab3. 00 01 01 ok
It should be 01 01 00

Ahmed
dxf
2024-10-08 07:33:40 UTC
Permalink
Not bad.  Here's a translation.  Hopefully it's equivalent (?)
: split ( a u c -- a2 u2 a3 u3 )
  >r 2dup r> scan 2swap 2 pick - ;
: number ( a u -- u )  0 0 2swap >number 2drop ;
: xx. ( u -- )  0 <# bl hold # # #> type ;
: tab3. ( h m s -- )  3 spaces ( tab)  rot xx. swap xx. xx. ;
: ts_elms ( a u -- h m s )
  2>r  0 0 0  2r>  begin
    [char] : skip  [char] : split  dup 0> while
    number drop  5 roll drop -rot
  repeat 2drop 2drop ;
s" 25"      ts_elms  tab3.    00 00 25  ok
s" 10:25"   ts_elms  tab3.    00 10 25  ok
s" 2:10:25" ts_elms  tab3.    02 10 25  ok
s" 1:1:"      ts_elms  tab3.        00 01 01  ok
It should be 01 01 00
Yes. It's an unlikely scenario. For a format that begins with seconds
that field must be filled first. Only when it overflows will a separator
and minutes field be added.
sjack
2024-10-08 15:19:25 UTC
Permalink
Yes, originally I had syntax checks, left them out to focus more on getting
the zeros in.

-- ts_elms ( "[hh:][mm:]ss<bl>" -- 0 0 ss | 0 mm ss | hh mm ss )
-- Parse timestamp elements: hh=hours mm=minutes ss=seconds
-- Input hh: element and hh:mm: combination elements may be left out
-- if zero(s).
: ts_elms
bl word here count
over c@ asc : = >r ( leading char check )
2dup + 1- c@ asc : = ( lagging char check )
r> or if ." --Invalid " 2drop rdrop exit then
o+s do i c@ asc : = if bl i c! then loop
0 0 0 here count
begin
bl split dup 0> while &num number drop
5 roll drop -rot
repeat 4drop
;

[s] Invalid syntax
i. ts_elms 25: tab3. --> --Invalid
i. ts_elms :25 tab3. --> --Invalid
i. ts_elms :25: tab3. --> --Invalid

[s] Valid syntax
ts_elms 25
i. tab3. --> 00 00 25
i. ts_elms 25 tab3. --> 00 00 25
i. ts_elms 10:25 tab3. --> 00 10 25
i. ts_elms 2:10:25 tab3. --> 02 10 25
sjack
2024-10-08 20:30:12 UTC
Permalink
Post by dxf
Not bad. Here's a translation. Hopefully it's equivalent (?)
FigForth NUMBER is a quirky beast. It starts on a counted string
but doesn't care about the count and it must end on a space.
It's why I replaced ":" with space before I split and then
split on space. Perhaps your NUMBER is better behaved.
dxf
2024-10-09 01:06:28 UTC
Permalink
Post by sjack
Post by dxf
Not bad. Here's a translation. Hopefully it's equivalent (?)
FigForth NUMBER is a quirky beast. It starts on a counted string
but doesn't care about the count and it must end on a space.
It's why I replaced ":" with space before I split and then
split on space. Perhaps your NUMBER is better behaved.
I have many versions as no single one can cover all possibilities.
In the kernel alone there is:

: (NUMBER) ( addr u -- ud addr2 u2 )
0 0 2swap >number ;

: /NUMBER ( addr u -- addr2 u2 d|ud )
/sign >r (number) 2swap r> if dnegate then ;

: NUMBER? ( addr u -- d|ud true | false )
dup if /number dpl on 2swap dup if 1- over c@
[char] . - or dpl off then nip 0= if true end
then 2drop false ;
a***@spenarnc.xs4all.nl
2024-10-08 07:41:07 UTC
Permalink
Post by dxf
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
This problem is ill posed. You don't specify what happens
with less that 3 fields, or the meaning of the fields.
Normally I make the tests to run before attempting to code.

Groetjes Albert
--
Temu exploits Christians: (Disclaimer, only 10 apostles)
Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
And Gifts For Friends Family And Colleagues.
dxf
2024-10-08 08:55:34 UTC
Permalink
Post by a***@spenarnc.xs4all.nl
Post by dxf
Is there an easier way of doing this? End goal is a double number
representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
This problem is ill posed. You don't specify what happens
with less that 3 fields, or the meaning of the fields.
Normally I make the tests to run before attempting to code.
Running this again I get the same (my expected) results (SwiftForth, VFX, Gforth etc).
Not sure how I concluded it was broken. I've lost track of the iterations done since.
Today I'm liking sjacks' solution.
Ahmed
2024-10-08 18:12:14 UTC
Permalink
Hi, Here is a programme that uses your split word.
I think it works for all combinations (formats).

\ here begins the code

: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;

: advance 1- swap 1+ swap ;

: .00?1
dup 1 3 within 0= if 2drop s" 00" then
;

: .00?2
dup 0= if 2drop s" 00" exit then
dup 1 3 within 0= if 2drop 2swap 2>r s" 00" 2swap 2r> then
;

: .00?3
dup 0= if 2drop s" 00" exit then
dup 1 3 within 0= if 2drop 2swap 2>r s" 00" 2rot 2rot 2r> then
;

: step1 [char] : split 2swap 2>r .00?1 2swap 2r> advance ;
: step2 [char] : split 2swap 2>r .00?2 2swap 2r> advance ;
: step3 [char] : split 2swap 2>r .00?3 2swap 2r> advance ;

: :t s" --" 2swap step1 step2 step3 2drop 2drop ;

: sts space type space ;
: .hr sts ." hr" ;
: .min sts ." min" ;
: .sec sts ." sec" ;

: .t 2>r 2swap .hr .min 2r> .sec ;
\ the code finshes here.

Some tests (gforth under wsl):

s" 10:20:30" :t .t 10 hr 20 min 30 sec ok
s" 1:20:30" :t .t 1 hr 20 min 30 sec ok

s" 1:2:30" :t .t 1 hr 2 min 30 sec ok
s" 1:2:3" :t .t 1 hr 2 min 3 sec ok
s" :2:3" :t .t 00 hr 2 min 3 sec ok
s" 2:3" :t .t 00 hr 2 min 3 sec ok
s" :3" :t .t 00 hr 00 min 3 sec ok
s" ::3" :t .t 00 hr 00 min 3 sec ok
s" ::" :t .t 00 hr 00 min 00 sec ok
s" :" :t .t 00 hr 00 min 00 sec ok
s" " :t .t 00 hr 00 min 00 sec ok
s" 1:" :t .t 00 hr 1 min 00 sec ok
s" :1:" :t .t 00 hr 1 min 00 sec ok
s" 10::" :t .t 10 hr 00 min 00 sec ok
s" 10:5:" :t .t 10 hr 5 min 00 sec ok
s" 10:5:2" :t .t 10 hr 5 min 2 sec ok

Ahmed

--
alaa
2024-10-09 15:27:46 UTC
Permalink
Hi,

here is another way, which was fun:

\
https://www.novabbs.com/devel/article-flat.php?id=28040&group=comp.lang.forth#28040
VOCABULARY timestamps-parser ALSO timestamps-parser DEFINITIONS

\ Idea: parse backward
VARIABLE a VARIABLE n
: >an n ! a ! ; : /an 0 a ! 0 n ! ;
: ch ( -- c ) a @ n @ + 1- C@ ;
: more? ( -- t/f ) n @ 0<> ;
: ch+ ( -- ) more? NOT ABORT" exceeded string" -1 n +! ;
: colon ( -- ) more? IF ch ':' <> ABORT" Expected colon!" ch+ THEN ;
: d? ( c -- t/f ) '0' '9' 1+ WITHIN ;
: digit? ( c -- t/f ) d? NOT ABORT" Expected a digit!" ;
: d ( -- ) ch digit? ch 48 - ch+ ;
: [d] ( -- n ) ch d? IF ch 48 - ch+ ELSE 0 THEN ;
: hr ( -- n ) [d] [d] 10 * + ; : min ( -- n ) [d] [d] 10 * + ; : sec (
-- n ) d [d] 10 * + ;
: parse ( a n -- s m h ) >an sec colon min colon hr /an ;
: .hr ( n -- ) ?DUP IF . ." hr " THEN ;
: .min ( n -- ) ?DUP IF . ." min " THEN ;
: .sec ( n -- ) ?DUP IF . ." sec " THEN ;
: .ts ( s m h -- ) .hr .min .sec ;

\ Examples
: e1 S" 1:2:3" ; CR e1 type e1 parse .s .ts
: e2 S" 02:03" ; CR CR e2 type e2 parse .s .ts
: e3 S" 03" ; CR CR e3 type e3 parse .s .ts
: e4 S" 23:59:59" ; CR CR e4 type e4 parse .s .ts
: e5 S" 0:00:03" ; CR CR e5 type e5 parse .s .ts
: e6 S" " ; CR CR e6 type e6 parse .s .ts \ will fail since we expect at
least one digit with 'd' word, we can use '[d]' for optional digit

\ another stupid idea/hack to try: count ':', replace them with BL,
Evaluate string, push missing fields as zeros
dxf
2024-10-10 01:17:57 UTC
Permalink
Post by alaa
Hi,
\
https://www.novabbs.com/devel/article-flat.php?id=28040&group=comp.lang.forth#28040
VOCABULARY timestamps-parser ALSO timestamps-parser DEFINITIONS
\ Idea: parse backward
VARIABLE a VARIABLE n
: >an n ! a ! ; : /an 0 a ! 0 n ! ;
: ch+ ( -- ) more? NOT ABORT" exceeded string" -1 n +! ;
: colon ( -- ) more? IF ch ':' <> ABORT" Expected colon!" ch+ THEN ;
: d? ( c -- t/f ) '0' '9' 1+ WITHIN ;
: digit? ( c -- t/f ) d? NOT ABORT" Expected a digit!" ;
: d ( -- ) ch digit? ch 48 - ch+ ;
: [d] ( -- n ) ch d? IF ch 48 - ch+ ELSE 0 THEN ;
: hr ( -- n ) [d] [d] 10 * + ; : min ( -- n ) [d] [d] 10 * + ; : sec (
-- n ) d [d] 10 * + ;
: parse ( a n -- s m h ) >an sec colon min colon hr /an ;
...
Would be useful where extra checking of fields is required. In that case
when ':' is detected, there ought to be at least one digit before it.
alaa
2024-10-10 16:30:33 UTC
Permalink
Yes.

You can customize it for added validations (i.e. sec is between 0 and
59, ...etc) and also extend it for other parsing tasks (i.e. dates [with
their different formats, long, short, with time, with zone], zip codes,
GPS, ...etc).
Hans Bezemer
2024-10-16 16:29:37 UTC
Permalink
Post by alaa
Hi,
\
https://www.novabbs.com/devel/article-flat.php?id=28040&group=comp.lang.forth#28040
VOCABULARY timestamps-parser ALSO timestamps-parser DEFINITIONS
\ Idea: parse backward
VARIABLE a VARIABLE n
: >an n ! a ! ; : /an 0 a ! 0 n ! ;
: ch+ ( -- ) more? NOT ABORT" exceeded string" -1 n +! ;
: colon ( -- ) more? IF ch ':' <> ABORT" Expected colon!" ch+ THEN ;
: d? ( c -- t/f ) '0' '9' 1+ WITHIN ;
: digit? ( c -- t/f ) d? NOT ABORT" Expected a digit!" ;
: d ( -- ) ch digit? ch 48 - ch+ ;
: [d] ( -- n ) ch d? IF ch 48 - ch+ ELSE 0 THEN ;
: hr ( -- n ) [d] [d] 10 * + ; : min ( -- n ) [d] [d] 10 * + ; : sec (
-- n ) d [d] 10 * + ;
: parse ( a n -- s m h ) >an sec colon min colon hr /an ;
: .hr ( n -- ) ?DUP IF . ." hr " THEN ;
: .min ( n -- ) ?DUP IF . ." min " THEN ;
: .sec ( n -- ) ?DUP IF . ." sec " THEN ;
: .ts ( s m h -- ) .hr .min .sec ;
\ Examples
: e1 S" 1:2:3" ; CR e1 type e1 parse .s .ts
: e2 S" 02:03" ; CR CR e2 type e2 parse .s .ts
: e3 S" 03" ; CR CR e3 type e3 parse .s .ts
: e4 S" 23:59:59" ; CR CR e4 type e4 parse .s .ts
: e5 S" 0:00:03" ; CR CR e5 type e5 parse .s .ts
: e6 S" " ; CR CR e6 type e6 parse .s .ts \ will fail since we expect at
least one digit with 'd' word, we can use '[d]' for optional digit
\ another stupid idea/hack to try: count ':', replace them with BL,
Evaluate string, push missing fields as zeros
Still another way, not complying to this specific implementation:

---8<---
3600 12 * constant (12hours) \ a constant holding 12 hours seconds

: h:m:s>secs ( a1 n1 -- n2)
['] is-type defer@ >r [: is-digit 0= ;] is is-type
0 >r 3600 >r -leading -trailing \ clean up string

begin \ split off all the components
dup \ if a null string, skip loop
while \ if not, convert it to a number
split> number error? dup >r \ if a number, scale it, add to
result
if drop else r> swap r> tuck * r> + >r 60 / >r >r dup if chop then
then r>
until r> drop \ drop scaling factor

dup if \ if remainder, unchop, clean
string up
1+ swap char- swap -leading 2dup s" PM" compare
if \ is it in the afternoon?
s" AM" compare if r> else r> dup (12hours) >= if (12hours) - then
then
else \ is it in the morning?
2drop r> dup (12hours) < if (12hours) + then
then \ then adjust accordingly
else \ if null string, discard it
2drop r> \ and just retrieve result
then r> is is-type
;
---8<---

Basically, it takes any non-digit as a delimiter, parses hours, minutes
and seconds and then checks for any AM or PM remaining, making
adjustments when required. It returns seconds. Those may easily be
combined with other epoch information or split into any required format.

Or be a component in this beauty:

: stamp>iso8601 ( a1 n1 -- a2 n2)
dup if
bl split trim 2swap trim 2>r s>date 2r> h:m:s>secs s>smh .datetime0
then
;

- Split the timestamp at the space;
- Convert the string to a date (d m y);
- Convert the other string to seconds;
- Convert the seconds to time (s m h);
- Push out a 8601 string.

Hans Bezemer
Gerry Jackson
2024-10-18 14:46:27 UTC
Permalink
Post by dxf
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
Another solution

: /t ( ca u -- sec min hour )
3 \ a count, decremented every recurse
[: -rot dup 0>
if 0. 2swap >number 1 /string 2swap drop ( -- ct ca' u' n1 )
Post by dxf
r rot 1-
recurse r> swap exit
then 2drop
;] execute
0 ?do 0 loop \ 0 hours and minutes if missing in source string
;
: .t cr . ." hr " . ." min " . ." sec " ;

cr
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
s" " /t .t
s" :" /t .t
s" :53" /t .t
s" 11/12/13" /t .t \ Different separator
s" 11::13" /t .t
s" :::" /t .t
s" 3:" /t .t
s" 1:2:" /t .t

\ Results
1 hr 2 min 3 sec
0 hr 2 min 3 sec
0 hr 0 min 3 sec
23 hr 59 min 59 sec
0 hr 0 min 3 sec
0 hr 0 min 0 sec
0 hr 0 min 0 sec
0 hr 0 min 53 sec
11 hr 12 min 13 sec
11 hr 0 min 13 sec
0 hr 0 min 0 sec
0 hr 0 min 3 sec
0 hr 1 min 2 sec

The last two could be regarded as wrong but you indicated elsewhere that
they wouldn't occur.

Any non-digit is a separator
--
Gerry
dxf
2024-10-19 01:29:18 UTC
Permalink
Post by Gerry Jackson
Is there an easier way of doing this?  End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 )  >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u )  0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
   2 0 do  [char] : split  2swap  dup if 1 /string then  loop
   2 0 do  dup 0= if 2rot 2rot then  loop ;
: .T  2swap 2rot  cr  >int . ." hr "  >int . ." min " >int . ." sec " ;
s" 1:2:3"    /t .t
s" 02:03"    /t .t
s" 03"       /t .t
s" 23:59:59" /t .t
s" 0:00:03"  /t .t
Another solution
: /t  ( ca u -- sec min hour )
   3        \ a count, decremented every recurse
   [: -rot dup 0>
      if 0. 2swap >number 1 /string 2swap drop ( -- ct ca' u' n1 )
         >r rot 1-
         recurse r> swap exit
      then 2drop
   ;] execute
   0 ?do 0 loop   \ 0 hours and minutes if missing in source string
;
: .t   cr . ." hr "  . ." min " . ." sec " ;
cr
s" 1:2:3"    /t .t
s" 02:03"    /t .t
s" 03"       /t .t
s" 23:59:59" /t .t
s" 0:00:03"  /t .t
s" " /t .t
s" :" /t .t
s" :53" /t .t
s" 11/12/13" /t .t   \ Different separator
s" 11::13" /t .t
s" :::" /t .t
s" 3:" /t .t
s" 1:2:" /t .t
\ Results
1 hr 2 min 3 sec
0 hr 2 min 3 sec
0 hr 0 min 3 sec
23 hr 59 min 59 sec
0 hr 0 min 3 sec
0 hr 0 min 0 sec
0 hr 0 min 0 sec
0 hr 0 min 53 sec
11 hr 12 min 13 sec
11 hr 0 min 13 sec
0 hr 0 min 0 sec
0 hr 0 min 3 sec
0 hr 1 min 2 sec
The last two could be regarded as wrong but you indicated elsewhere that they wouldn't occur.
In practice several others too as separators in isolation are unlikely.
Post by Gerry Jackson
Any non-digit is a separator
That prompted me to look at sjack's code again and found SPLIT could be omitted:

: (number) ( a u -- ud a' u' ) 0 0 2swap >number ;

: /int ( a u -- a' u' u2 ) (number) 2swap drop ;

: .t cr . ." hr " . ." min " . ." sec " ;

: /t ( a u -- sec min hr )
2>r 0 0 0 2r> begin
/int 5 roll drop -rot dup while 1 /string
repeat 2drop swap rot ;


1 hr 2 min 3 sec ok
0 hr 2 min 3 sec ok
0 hr 0 min 3 sec ok
23 hr 59 min 59 sec ok
0 hr 0 min 3 sec ok
0 hr 0 min 0 sec ok
0 hr 0 min 0 sec ok
0 hr 0 min 53 sec ok
11 hr 12 min 13 sec ok
11 hr 0 min 13 sec ok
0 hr 0 min 0 sec ok
0 hr 3 min 0 sec ok
1 hr 2 min 0 sec ok
Hans Bezemer
2024-10-28 17:07:32 UTC
Permalink
Post by dxf
Post by Gerry Jackson
Post by dxf
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
Another solution
: /t ( ca u -- sec min hour )
3 \ a count, decremented every recurse
[: -rot dup 0>
if 0. 2swap >number 1 /string 2swap drop ( -- ct ca' u' n1 )
Post by dxf
r rot 1-
recurse r> swap exit
then 2drop
;] execute
0 ?do 0 loop \ 0 hours and minutes if missing in source string
;
: .t cr . ." hr " . ." min " . ." sec " ;
cr
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
s" " /t .t
s" :" /t .t
s" :53" /t .t
s" 11/12/13" /t .t \ Different separator
s" 11::13" /t .t
s" :::" /t .t
s" 3:" /t .t
s" 1:2:" /t .t
\ Results
1 hr 2 min 3 sec
0 hr 2 min 3 sec
0 hr 0 min 3 sec
23 hr 59 min 59 sec
0 hr 0 min 3 sec
0 hr 0 min 0 sec
0 hr 0 min 0 sec
0 hr 0 min 53 sec
11 hr 12 min 13 sec
11 hr 0 min 13 sec
0 hr 0 min 0 sec
0 hr 0 min 3 sec
0 hr 1 min 2 sec
The last two could be regarded as wrong but you indicated elsewhere
that they wouldn't occur.
Post by dxf
In practice several others too as separators in isolation are unlikely.
Post by Gerry Jackson
Any non-digit is a separator
: (number) ( a u -- ud a' u' ) 0 0 2swap >number ;
: /int ( a u -- a' u' u2 ) (number) 2swap drop ;
: .t cr . ." hr " . ." min " . ." sec " ;
: /t ( a u -- sec min hr )
2>r 0 0 0 2r> begin
/int 5 roll drop -rot dup while 1 /string
repeat 2drop swap rot ;
1 hr 2 min 3 sec ok
0 hr 2 min 3 sec ok
0 hr 0 min 3 sec ok
23 hr 59 min 59 sec ok
0 hr 0 min 3 sec ok
0 hr 0 min 0 sec ok
0 hr 0 min 0 sec ok
0 hr 0 min 53 sec ok
11 hr 12 min 13 sec ok
11 hr 0 min 13 sec ok
0 hr 0 min 0 sec ok
0 hr 3 min 0 sec ok
1 hr 2 min 0 sec ok
I have put the complication elsewhere. If we assume we're working in
decimal, you don't even need >NUMBER:

char 0 negate +constant 0-

: /int ( a1 n1 -- a2 n2 n3)
0 >r 1 >r 1- chars over +
begin
over 1- over <
while
dup c@ is-digit
while
dup c@ 0- r> tuck * r> + >r 10 * >r 1-
repeat over - rdrop r> -rot
;

: /t /int /int /int 2drop ;

: .t . ." hr " . ." min " . ." sec " cr ;

Notes for 4tH-isms:
- : 0- [char] 0 - ;
- : is-digit [char] 0 [char] 9 1+ within ;
- Add an additional THEN after REPEAT;
- RDROP, replace with R> DROP.

pp4th -x timesplit.4th
1 hr 2 min 3 sec
0 hr 2 min 3 sec
0 hr 0 min 3 sec
23 hr 59 min 59 sec
0 hr 0 min 3 sec
0 hr 0 min 0 sec
0 hr 0 min 0 sec
0 hr 0 min 53 sec
11 hr 12 min 13 sec
11 hr 0 min 13 sec
0 hr 0 min 0 sec
0 hr 3 min 0 sec
1 hr 2 min 0 sec

Hans Bezemer
dxf
2024-10-29 09:25:24 UTC
Permalink
Post by Hans Bezemer
...
char 0 negate +constant 0-
: /int    ( a1 n1 -- a2 n2 n3)
  0 >r 1 >r 1- chars over +
  begin
   over 1- over <
  while
  while
  repeat over - rdrop r> -rot
;
...
So that's how to convert a numeric string from the other end.
Nice! Easier than I imagined it would be.
Hans Bezemer
2024-10-29 13:45:34 UTC
Permalink
Post by dxf
Post by Hans Bezemer
...
char 0 negate +constant 0-
: /int    ( a1 n1 -- a2 n2 n3)
  0 >r 1 >r 1- chars over +
  begin
   over 1- over <
  while
  while
  repeat over - rdrop r> -rot
;
...
So that's how to convert a numeric string from the other end.
Nice! Easier than I imagined it would be.
TORS is the multiplier, 2ORS is the accumulator. Normally, the
multiplier is multiplied after each run with BASE @ - but that makes
little sense when IS-DIGIT isn't properly adjusted. Would be a nice
exercise, though.

Hans Bezemer
dxf
2024-10-30 00:31:10 UTC
Permalink
Post by dxf
Post by Hans Bezemer
...
char 0 negate +constant 0-
: /int    ( a1 n1 -- a2 n2 n3)
   0 >r 1 >r 1- chars over +
   begin
    over 1- over <
   while
   while
   repeat over - rdrop r> -rot
;
...
So that's how to convert a numeric string from the other end.
Nice!  Easier than I imagined it would be.
Something like this...

\ \CHAR ( a u -- a u-1 c )
\ >DIGIT ( c base -- u -1 | c 0 )

: /INT ( a1 n1 -- a2 n2 u )
0 >r 1 begin >r dup while
\char base @ >digit while
r> tuck * r> + >r base @ *
repeat drop ( -1 /string) then rdrop r> ;

OTOH the necessity to convert R-L is probably rare.
Hans Bezemer
2024-10-30 08:37:41 UTC
Permalink
Post by dxf
Post by dxf
Post by Hans Bezemer
...
char 0 negate +constant 0-
: /int    ( a1 n1 -- a2 n2 n3)
   0 >r 1 >r 1- chars over +
   begin
    over 1- over <
   while
   while
   repeat over - rdrop r> -rot
;
...
So that's how to convert a numeric string from the other end.
Nice!  Easier than I imagined it would be.
Something like this...
\ \CHAR ( a u -- a u-1 c )
\ >DIGIT ( c base -- u -1 | c 0 )
: /INT ( a1 n1 -- a2 n2 u )
0 >r 1 begin >r dup while
repeat drop ( -1 /string) then rdrop r> ;
OTOH the necessity to convert R-L is probably rare.
Yes, R-L number parsing is very rare. This time, it felt like the
easiest solution, though. Equally when you're parsing a non-decimal
date. That's why I left it out here. But it doesn't hurt to contemplate
such things. ;-)

I got the words you describe as "STRING/C" (rings a bell?) and "DIGIT?".
Seem like natural candidates to tackle this problem.

Hans Bezemer
dxf
2024-10-31 00:41:23 UTC
Permalink
Post by dxf
Post by dxf
Post by Hans Bezemer
...
char 0 negate +constant 0-
: /int    ( a1 n1 -- a2 n2 n3)
    0 >r 1 >r 1- chars over +
    begin
     over 1- over <
    while
    while
    repeat over - rdrop r> -rot
;
...
So that's how to convert a numeric string from the other end.
Nice!  Easier than I imagined it would be.
Something like this...
\  \CHAR ( a u -- a u-1 c )
\  >DIGIT ( c base -- u -1 | c 0 )
: /INT ( a1 n1 -- a2 n2 u )
   0 >r  1  begin  >r  dup while
   repeat  drop ( -1 /string)  then  rdrop  r> ;
OTOH the necessity to convert R-L is probably rare.
Yes, R-L number parsing is very rare. This time, it felt like the easiest solution, though. Equally when you're parsing a non-decimal date. That's why I left it out here. But it doesn't hurt to contemplate such things. ;-)
I got the words you describe as "STRING/C" (rings a bell?) and "DIGIT?". Seem like natural candidates to tackle this problem.
Char extraction occurs often that it made sense to make them bona-fide words.
Having them in the kernel meant I could code them in asm for extra speed.

It raises the question of whether it's better to test then extract - or extract
then test. The latter is simpler but it can mean one has to undo the operation.
That was the purpose of ( -1 /string) ... which BTW was incorrect; it should have
been ( 1+) .

Loading...