( Title: Dynamic String Utilities Author: David N. Williams File: dstring-utils.fs Test file: dstring-utils-test.fs Log file: dstring-utils.log License: LGPL Version: 0.8.6 Revised: October 21, 2010 ) \ Copyright (C) 1994-2004, 2006-2010 by David N. Williams ( This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or at your option any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. If you take advantage of the option in the LGPL to put a particular version of this library under the GPL, the author[s] would regard it as polite if you would put any direct modifications under the LGPL as well, and include a copy of this request near the beginning of the modified library source. A "direct modification" is one that enhances or extends the library in line with its original concept, as opposed to developing a distinct application or library which might use it. ) ( This library works with version 0.7.6 or later of the Dynamic-Strings word set, either the pfe implementation or the ANS Forth implementation in dstrings.fs. It evolves various words we have used, some from a long while back. The code is generally Forth 200x compatible, if dstrings.fs is used, except for case dependence. The word FNUM+ has been tested with a separate and with a nonseparate floating point stack. The constant MAX-FNUM-DIGS may be defined prior to loading this library if its default value is not suitable. ) decimal [UNDEFINED] USE-DSTRINGS-EXT [IF] false CONSTANT USE-DSTRINGS-EXT [THEN] USE-DSTRINGS-EXT [IF] LOADM dstrings [ELSE] s" dstrings.fs" included [THEN] [UNDEFINED] USE-FPNOSTACK-EXT [IF] false CONSTANT USE-FPNOSTACK-EXT [THEN] [UNDEFINED] USE-FP [IF] false CONSTANT USE-FP [THEN] USE-FP [IF] USE-FPNOSTACK-EXT [IF] LOADM fpnostack [ELSE] s" FLOATING-EXT" environment? [IF] ( flag) drop [ELSE] cr .( FLOATING POINT NOT AVAILABLE) cr ABORT [THEN] [THEN] [UNDEFINED] MAX-FNUM-DIGS [IF] 256 CONSTANT MAX-FNUM-DIGS [THEN] [THEN] \ *** WORDS ( NUMBER AND DATE CATS num+ unum0r+ unum+ unum>hex+ -mon- date&time+ MAX-FNUM-DIGS set-fnum-digs fnum+ PRIVATE fnum-digs ) \ *** UTILITY WORDS [UNDEFINED] num>s [IF] : num>s ( n -- n.s ) \ Comus aka (.) ( Convert the number according to the current BASE to an ANS Forth string in transient memory. Traditional implementation. ) <# dup >r abs s>d #s r> sign #> ; [THEN] [UNDEFINED] unum>s [IF] : unum>s ( u -- u.s ) ( Convert the unsigned number according to the current BASE to an ANS Forth string in transient memory. ) <# 0 #s #> ; [THEN] \ *** NUMBER AND DATE CATS ( "Cat" refers to dstring concatenation. Words whose action is to cat have a "+" appended to their names. The cats in the following are "cat clean", that is, they do not include code with terminated subcats, so they may be used in succession. Earlier versions of this library included words that produced strings on the string stack. They have been removed, some because they are easy to define in terms of cat words, others because they could lead to unintended cat fights, and should be defined in the context of a controlled, current cat state. ) : num+ ( n -- ) ( Cat the number as a string in the current BASE. ) num>s s+ ; : unum+ ( u -- ) ( Cat the unsigned number as a string in the current BASE. ) unum>s s+ ; : unum>hex+ ( u -- ) ( Cat the unsigned number as a hexadecimal string, prefixed by "0x". ) base @ >r $+" 0x" hex unum>s s+ r> base ! ; : unum0r+ ( u r -- ) ( Cat the last r digits of the nonnegative number as a string in the current BASE. If the number has fewer than r digits, fill with leading zeroes to make r digits. If it has more, the output is silently truncated. ) swap num>s \ assume no "-" rot over - ( r-#digits) dup 0> IF \ need leading "0"'s ( r-#digits) 0 DO $+" 0" LOOP ELSE \ simply truncate negate ( #digits-r) /string THEN s+ ; : -mon- ( month -- month.s ) ( Leave the month string corresponding to month = 1,2,... ) 1- 4 chars * [ s" -Jan-Feb-Mar-Apr-May-Jun-Jul-Aug-Sep-Oct-Nov-Dec-" ] sliteral drop + 5 chars ; : date&time+ ( -- ) ( Cat the current date and time as a string with "0"-filled fields, as in "04-Jul-2002 09:02:06". ) time&date LOCALS| yr mo day hr min sec | day 2 unum0r+ mo -mon- s+ yr num+ $+" " hr 2 unum0r+ $+" :" min 2 unum0r+ $+" :" sec 2 unum0r+ ; USE-FP [IF] create digs-pad MAX-FNUM-DIGS chars allot \ PRIVATE 6 value fnum-digs \ not the same effect as PRECISION \ PUBLIC : set-fnum-digs ( u -- ) dup MAX-FNUM-DIGS u> ABORT" ***SET-FNUM-DIGS overflow" to fnum-digs ; [UNDEFINED] +INF [IF] 1e 0e f/ FCONSTANT +INF [THEN] [UNDEFINED] FINITE? [IF] \ assume basic IEEE-FP arithmetic for specials : FINITE? ( f: r -- ) ( -- [normal|subnormal]? ) fabs +INF f< ; [THEN] : fnum+ ( f: num -- ) ( Cat the number according to the elements in the ANS Forth specification for REPRESENT. The result has FNUM-DIGS to the right of the decimal point, with the leading digit just to the right, and no digits to the left. The fstack may be separate or unified. ) fdup finite? 0= IF #-43 THROW THEN base @ >r decimal ( f: num) digs-pad fnum-digs represent r> base ! ( exp minus? valid?) drop ( minus?) IF $+" -." ELSE $+" ." THEN ( exp) >r digs-pad fnum-digs s+ $+" E" r> ( exp) num+ ; [THEN] \ USE-FP