'=== Full description for the IndexFormat$() function is available
'=== in the separate HTML document.
'=====================================================================
'-- The following format templates need its arguments in different order,
'-- no problem with indexing, no need to reorder the given arguments.
'-- You may even use different formatting for the same argument, as long
'-- as its types are compatible (ie. string vs. number).
dateDE$ = "Date format Germany: 0{#}. 1{&} 2{####}" ' 1{} = full string
dateUS$ = "Date format US : 1{\ \}/0{#} 2{####}" '1{} = first 3 chars only
'-- The easiest way to pass a variable number of arguments, which may
'-- even be of different types, to a user function is using a string.
'-- All arguments will be concatenated in this string, separated by a
'-- designated char/string which does not appear in the arguments itself.
'-- Strings can be added as is, numbers can be added as literal strings
'-- too, or in the form STR$(variable).
year% = 2021
argStr$
= "2|Januar|" + STR$(year%
)'-- In this example the | is the argument separator. Use whatever is
'-- suitable for your needs, maybe even a CHR$(0).
'-- Now let's test the whole thing, we've different token orders in the
'-- format templates, but use the same argument string for both calls.
PRINT IndexFormat$
(dateDE$
, argStr$
, "|") PRINT IndexFormat$
(dateUS$
, argStr$
, "|")
'-- And here the examples from the function description, which also
'-- shows the reuse of arguments without the need to pass more arguments
'-- for the additional "feet" and "toes" format tokens.
head = 1: hands = 2: fingers = 10
PRINT USING "## head, ## hands and ## fingers"; head
, hands
, fingers
PRINT USING "## fingers, ## head and ## hands"; head
, hands
, fingers
argStr$
= STR$(head
) + "|" + STR$(hands
) + "|" + STR$(fingers
)PRINT IndexFormat$
("2{##} fingers, 0{##} head and 1{##} hands", argStr$
, "|") PRINT IndexFormat$
("0{##} head, 1{##} hands and 2{##} fingers, also 1{##} feet and 2{##} toes", argStr$
, "|")
'-- The function can also handle escape sequences as known from C/C++,
'-- so you may use those sequences within your format templates.
PRINT IndexFormat$
("Column-1\tColumn-2\tColumn-3\n0{#.##}\t\t1{#.##}\t\t2{#.##}", "1.11|2.22|3.33", "|") PRINT IndexFormat$
("This is a \x220{&}\x22 section.", "quoted", "|") '-- Using escape sequences and the new bin/dec/hex/oct/real formatting,
'-- while reusing the same argument for all tokens. Also showing the use
'-- of preferences specifiers to group bin and hex outputs.
PRINT IndexFormat$
(" Bin: 0{?4:B16}\n Dec: 0{D}\n Hex: 0{?2:H8}\n Oct: 0{O}\nReal: 0{R}\n", "2021.00548", "|")
'-- Alignment of strings in a fixed length field, the square brackets are
'-- just used to better visualize the field.
PRINT IndexFormat$
("[0{?L:\ \}]", "RhoSigma", "|") PRINT IndexFormat$
("[0{?C:\ \}]", "RhoSigma", "|") PRINT IndexFormat$
("[0{?R:\ \}]", "RhoSigma", "|")
'-- Finally a currency example with replaced dollar sign and flipped
'-- comma/dot notation. I'd like to get that much for this function ;)
PRINT IndexFormat$
("Account balance: 0{?î,:**$#####,.##}", "12345.67", "|")
'-- done
'--- Full description available in separate HTML document.
'---------------------------------------------------------------------
FUNCTION IndexFormat$
(fmt$
, arg$
, sep$
) '--- option _explicit requirements ---
DIM args$
, shan&
, dhan&
, than&
, idx%
, cpos&
, res$
, lit%
, tok%
, ft$
, cch$
DIM och$
, opos&
, tmp$
, fp$
, tyl%
, typ$
, oval&&
, temp~&&
, curr%
, high%
'--- init ---
args$ = arg$ 'avoid side effects
REDIM argArr$
(0 TO 35) 'all args empty '--- parse arguments ---
argArr$
(idx%
) = LEFT$(args$
, cpos&
- 1) args$
= MID$(args$
, cpos&
+ LEN(sep$
))'--- process format template ---
res$ = "": lit% = 0: tok% = 0: ft$ = "": idx% = -1
cch$
= MID$(fmt$
, cpos&
, 1) IF cch$
= "_" AND lit%
= 0 THEN 'take next \{} as literal CASE "V": och$
= CHR$(11) 'vertical tabulator CASE "R": och$
= CHR$(13) 'carriage return CASE "0", "1", "2", "3" ' octal ASCII (3 digits) cpos& = cpos& + 2
CASE "X" ' hex ASCII (x + 2 digits) cpos& = cpos& + 2
res$ = res$ + och$
cpos& = cpos& + 1: opos& = cpos&
och$
= UCASE$(MID$(fmt$
, cpos&
- 1, 1)): tok%
= -1 IF ((cpos&
- 1) = opos&
) OR ((och$
< "0" OR och$
> "9") AND (och$
< "A" OR och$
> "Z")) THEN och$
= "-" GOSUB doArgFormat: res$
= res$
+ tmp$
tok% = 0: ft$ = "": idx% = -1
ELSE 'accumulate chars/symbols in correct channel IF tok%
THEN ft$
= ft$
+ cch$:
ELSE res$
= res$
+ cch$
lit% = 0
'--- cleanup & set result ---
IndexFormat$ = res$
'-----------------------------
doArgFormat:
tyl%
= INSTR(2, ft$
, ":") CASE "!", "&", "\" 'regular string formatting tyl%
= INSTR(2, ft$
, "\"):
IF tyl%
= 0 THEN ft$
= "\" + ft$: tyl%
= 2 CASE "C", "c": tyl%
= (tyl%
- LEN(argArr$
(idx%
))) \
2 CASE "R", "r": tyl%
= tyl%
- LEN(argArr$
(idx%
)) CASE ELSE: tyl%
= 0 'L or Unknown is default (left) argArr$
(idx%
) = SPACE$(tyl%
) + argArr$
(idx%
) CASE "B", "D", "H", "O", "R" 'extended number formatting (bin/dec/hex/oct/real) IF tyl%
> 0 THEN 'adjust field length (if any) IF typ$
<> "E" AND typ$
<> "D" THEN tmp$
= "-" + LEFT$(tmp$
, idx%
- 1) + MID$(tmp$
, idx%
+ 1) tmp$ = "%" + tmp$
typ$ = "": tyl% = 0
typ$
= MID$(tmp$
, idx%
, 1) + typ$: tyl%
= tyl%
+ 1 IF tyl%
= VAL(fp$
) THEN typ$
= " " + typ$: tyl%
= 0 CASE ELSE 'regular number formatting (or invalid nonsense) PRINT ft$;
'take nonsense as is tmp$ = tmp$ + ft$
'-----------------------------
doBinString:
oval&&
= VAL(argArr$
(idx%
)): temp~&&
= oval&&
tmp$
= STRING$(64, "0"): curr%
= 64: high%
= 64 IF (temp~&&
AND 1) THEN MID$(tmp$
, curr%
, 1) = "1": high%
= curr%
curr% = curr% - 1: temp~&& = temp~&& \ 2
IF -oval&&
< &H0080000000~&&
THEN high%
= 33 IF -oval&&
< &H0000008000~&&
THEN high%
= 49 IF -oval&&
< &H0000000080~&&
THEN high%
= 57