Namespace: PSU_ Machine Code Ext.s - psu_ S(uper)BASIC PROCedures/FuNctions
(PSU: Peter Sulzer Utilities)

Version 2.0.0 2026-04-06 Mon

Copyright (c) 2025, 2026, Peter Sulzer, Frth (Fuerth) - all rights reserved
Licence: See file licence_txt.

PSU_FPARSE%(expression_convertible_to_string, initialised_float_variable)
(parameter 1 maybe a variable like string$, '1.2', a float or integer variable
or even any expression that can be converted to a string like 3<4(!). But it's
recommended that you pass a string expression. The function result is the
the number of characters which could be converted to a float or a negative
error number (e.g. -17 for error in expression). Be careful if your number
ends with 'e' or 'E', this cannot be converted. After 'e' a digit MUST
follow(!). This is IMHO a fault of CN.DTOF as a number ending with a dot
is okay. So e.g. '9eleven' should IMO be converted to 9 (result=1). 

There are a few cases where Minerva (QDOS? - not yet tested)
behaves differently than SMSQ/E's SBASIC, please see the unit
tests in "fparsV2Utest_bas". There's IMO no chance to correct this
as CN.DTCONV behaves differently on SMSQ/E, e.g. Minerva 1.98
converts '1.2.1' because of a bug (result 3), all other versions
(e.g. Minerva 1.97) return -17 (error in exprssion).

The other problem are omitted parameters or if a parameter is an
unset variable. On SMSQ/E "unset" variables are automatically
given (assigned?) a value of 0. BUT THEY ARE STILL UNASSIGNED!

Example:
CLEAR:PRINT PSU_FPARSE%('1.23',unset)!unset
REMark Output: 4 0
PRINT PSU_FPARSE%('1.23',unset)!unset
REMark Surprise, output is again: 4 0.
REMark On Minerva an asterisk ('*') would be output instead of the
       '0'(!).
unset=0:REMark Just now(!) variable 'unset' is initialised
PRINT PSU_FPARSE%('1.23',unset)!unset
REMark Now the output is: 4 1.23

This new version will (famous last words) NEVER abort with an
error, but always returns either an detected error (integer -15
[bad parameter] or -17 [error in expression]) or the number of
converted characters to the caller). PSU_FPARSE%() does NOT return
an error for:

PRINT #0, PSU_FPARSE%('1.23',-9.99)
4

I.e. the 4 characters of the string could be converted, but of
course it cannnot assign anything to the string expression. This
cannot be corrected, cause the QDOS vector BP.LET does NOT return
an error when trying to assign to a parameter which has been
passed as an expression (by value). To make Minerva and SMSQ/E as
compatible as possible, PSU_FPARSE%('1.23',unset_variable) will
return -15 (invalid parameter) even on SMSQ/E, albeit it would be
possible to assign to an "unset variable" on SMSQ/E. On
Minerva/QDOS if you fetch an unset variable with CA.GTFP in a
machine code extension, the error returned (in D0) is -15 (bad
parameter). It must be possible to assign to an unset variable in
a machine code procedure as TK2's GET can do it, but I don't know
how to do it.

Recommendation: Because of the differences of the vector CN.DTOF
(which PSU_FPARSE%() uses to convert a string to a float) on
SMSQ/E and Minerva, one should test if the conversion was
successful with:

10 a=float:REMark The float parameter (1st parameter)must be initialised(!)
20 result%=PSU_FPARSE%(STRING_EXPRESSION,a)
30 IF result% < 1
31 REMark     ^^^ 0 characters were converted, i.e. no conversion possible
40   PRINT'String could not be converted to a float, a not altered'
50 ELSE
60   PRINT 'First'!result%!' characters of String converted to float, a=';a
70 END IF

W A R N I N G :  There is a problem with QDOS vector CN.DTOF on
none SMSQ/E sytems (at least Minerva 1.98) which is used to
convert (parse) a string to a float. See end note 1) (CN.DTOF
Documentation) below.

Examples

a=99:rslt%=PSU_FPARSE%('1',a):REMark Now rslt%=1, a=1
a=99:rslt%=PSU_FPARSE%('1.',a):REMark Now rslt%=2, a=1
a=99:rslt%=PSU_FPARSE%('1.23',a):REMark Now rslt%=4, a=1.23
a=99:rslt%=PSU_FPARSE%('1.23 2 3 4',a):REMark Now rslt%=4, a=1.23
rslt%=rslt%+PSU_FPARSE%(s$(1+rslt%TO),a):REMark Now rslt%=6, a=2
rslt%=rslt%+PSU_FPARSE%(s$(1+rslt%TO),a):REMark Now rslt%=8, a=3
...
a=99:rslt%=PSU_FPARSE%('1.Rest_of_string',a):REMark Now rslt%=2, a=1
a=99:rslt%=PSU_FPARSE%('1e',a):REMark Now rslt%=-17, a=99(!)
a=99:rslt%=PSU_FPARSE%('1eRest_of_string',a):REMark Now rslt%=-17, a=99(!)
a=99:rslt%=PSU_FPARSE%('1e0Rest_of_string',a):REMark Now rslt%=3, a=1
a=99:rslt%=PSU_FPARSE%(3.6,a):REMark Now rslt%=1, a=3.6
a=99:rslt%=PSU_FPARSE%('3+4',a):REMark Now rslt%=1, a=3
a=99:rslt%=PSU_FPARSE%(3+4,a):REMark Now rslt=1, a=7
CLEAR:a=99:rslt%=PSU_FPARSE%(s$,a):REMark Now rslt%=-15, a=99
DIM arr(3):a=99:rslt%=PSU_FPARSE%(arr,a):REMark Now rslt%=-15, a=99
rslt%=PSU_FPARSE%('1.23',):REMark Now rslt%=-15
rslt%=PSU_FPARSE%('1.23',):REMark Now rslt%=-15
rslt%=PSU_FPARSE%('1.23'):REMark Now rslt%=4
  (Next line aborts [ONLY!] on SMSQ/E (QPC2) with error in expression)
s$='':a=99:rslt%=PSU_FPARSE('1',s$):REMark Now rslt%=-15, a=99
CLEAR:rslt%=PSU_FPARSE('1',a):REMark Now rslt%=-15

For more examples see the unit tests "fparsV2Utest_bas" (requires
VALIDI_bin from knoware.no)

A simple example how to use PSU_FPARSE% is e.g:

INPUT'Enter a float: ';var$
DEFAULT_VALUE='-1.6158502672999e616'
a=DEFAULT_VALUE:result%=PSU_FPARSE(var$,a)
IF result%<1:PRINT var$!'cannot be converted to float':END IF
REMark If var$ couldn't be converted a=DEFAULT_VALUE

Variable a is now value of var$ (if convertable to float) or the
DEFAULT_VALUE. THE SECOND PARAMETER (variable which is assigned)
MUST BE INITIALISED(!).

After implementing it (with CN.DTOF) I realised tha CN.DTOF
returns the position of the next character which has not been
converted. So since version 1.04 PSU_FPARSE%() returns NOT 0 if
the conversion was successful, but the number of characters from
the first parameter which have been converted successfully.

This allows (finally) array initialisation (currently only
one-dimensional arrays/array slices). See my example program
"arrini1_bas". It does initialise a floating point array. E.g.:

DIM arr(3,3)
rslt=psu_farray_init1%(arr(2),'1 2 3 4.5e 3)')
PRINT arr(2) :REMark                 ^^^^^^ Surprise (= 4500)
REMark Outputs 1 2 3 4500 (replace spaces with linefeeds)

The example program also includes FuNction
psu_eat_spc%(string$,pos) which returns the first position in
string$, starting from string$(pos), which is no space.

I wanted to write a similar function to initialise
multidimensional arrays. This is unfortunately much more complex,
than I have thought, so this has been left open as an exercise for
the user <GRIN>. You can use psu_farray_init1%() with array slices
as the above example shows.

However the main reason why I wrote PSU_FPARSE%() is that,
together with the VALID%() funtion from knoware.no
(https://www.knoware.no/htm/toolkits.htm), it makes it possible to
detect implicitly omitted parameters or convert parameters passed
as strings into floats without the program crashing with an error.
See the tests in the unit test program "fparsV2Utest_bas", which
call the function test_paraf(). Here another simple example:

100 DEFine FuNction indent$(s_$,flt_$)
110   REMark for flt_$ a number (float/integer) should be passed(!)
120   LOCal typ,indent%,flt,rslt%,slen%
130   indent%=2:flt=0
140   typ=VALID%(-1,flt_$)
150   SELect ON typ
160   =513
170     REMark Omitted or passed as string
180     slen%=LEN(s_$)
190     rslt%=PSU_FPARSE%(flt_$,flt)
200     IF rslt%>=0
210       IF flt>=0AND flt<=32765-slen%:indent%=INT(flt):END IF
220      END IF
230   =514
240     REMark Passed as float
250     slen%=LEN(s_$)
260     IF flt_$>=0AND flt_$<=32765-slen%:indent%=INT(flt_$):END IF
270   =515
280     REMark Passed as integer
290     indent%=flt_$
300   END SELect
310   RETurn FILL$(' ',indent%)&s_$
320 END DEFine

PRINT#0,indent$('Test')
  Test
PRINT#0,indent$('Test','Not convertable to a float')
  Test
PRINT#0,indent$('Test','4.3rest of convertable string')
    Test
PRINT#0,indent$('Test',4.7)
    Test
PRINT#0,indent$('Test',-1):REMark or ...,'-1')
  Test
PRINT#0,indent$('Test',32761):REMark or ...,'32761')
[LOT OF EMPTY LINES...]
 Test
PRINT#0,indent$('Test',32762):REMark or ...,'32762')
  Test
inum%=3:PRINT#0,indent$('Test',inum%)
   Test
dim arr(3):PRINT#0,indent$('Test',arr)
  Test


TODO:

1
Allow to pass an uninitialised float, which will be set. It must
be possible as e.g. INPUT does it, but I don't know how.

2
Allow more than one float parameter or one dimension of an array.
For assigning one dimension of an array you can use the
S(uper)BASIC function "psu_farray_init1%(arr_,ini_$)" in the
example program "arrini1_bas". Excercise for the user: Write a
function which allows assigning multidimensional float arrays.

Examples:

rslt%=PSU_FPARSE%('1 2 3',a,b,c,d)
REMark Now a=1, b=2, c=3, d=0(SBASIC)/d=uninitialised (SuperBASIC)
REMark rslt%=5
rslt%=PSU_FPARSE%('1 2 3',x,y)
REMark Now x=1, y=2, rslt%=3


-- 
1) CN.DTOF Documentation

On none SMSQ/E systems (at leastMinerva V1.98) there is a problem
if the string which should be parsed is preceeded by spaces. In
this case CN.DTOF advances the buffer pointer to the string in
A0.l to the first non space character and returns 0 in D0.l. This
is problematic (if not bullshit) cause this would be also the case
for a string which can be parsed to a float, e.g. '12abcde' (which
would be parsed to 12). Even more worse, it puts a float of value
0 (6 bytes) on the RI stack pointed to by A1.l.

The first version (1.0.1) therefore returned false results if the
string could not be converted to a float, e.g.:

a=99:PRINT PSU_FPARSE%(a,'  abc')!a:REMark Output 2 0
a=99:PRINT PSU_FPARSE%(a,'abc')!a:REMark Output 0 0

This has been corrected since Version 1.0.4, output is now in both
above examples: -17 99 (-17: error in expression)

Unfortunately there are still differences between SMSQ/E (QPC2)
and (only) Minerva 1.98, e.g.

a=99:PRINT PSU_FPARSE%(a,'1..1')
Output SMSQ/E (QPC2): -17 99
Output Minerva 1.98: 2 1

In Technical Guide is documented that QDOS behaves like SMSQ/E, i.e.
Minverva 1.98 has a bug (see below). But:

a=99:PRINT PSU_FPARSE%(a,'1.e')

Outputs -17 99 on Minerva and SMSQ/E. After 'e' or 'E' in a string
where the first part can be parsed to a float, at least one digit
or '-' plus digit(s) (no spaces between e and '-' and/or digit
allowed!) must follow after the 'e'/'E'.

(Following for PSU_FPARSE version 1.0.4 only:
See also the new unit test BASIC program fparsUtest3_bas tests 15
and 16.)

Minerva 1.98 has a bug when converting to float, example:

a='abc':PRINT a

Output is 0. All other ROM versions will abort with error in
expression. See also:

https://theqlforum.com/viewtopic.php?t=5650&sid=8516132411a85a9696e6a46b8971fc2

