Oxygen Basic
Information => Development => Topic started by: Mike Lobanovsky on September 18, 2014, 01:09:37 PM
-
Why don't we have customary LBound, UBound and Count defined for OxygenBasic arrays?
I refuse to keep track of an array's Indexbase in various scopes on a scrap of paper lying beside my keyboard. I refuse to be deprived of any possibility to poll that very Indexbase at run time whenever I want. I need a good old LBound for that.
I refuse to understand SpanOf an array which, according to the .CHM help file, should return mysterious nbytes:
nbytes=spanof variable dimension
but apparently returns the number of elements in that array, that is its Count. To me, a "span" is something that implies "from ... to" but I see nothing of either "from" or "to" kind in those nbytes or SpanOf itself.
I refuse to code loops not knowing exactly how tall my arrays are. I cannot know or deduce where I should stop if I can't poll the actual base of my array and all I know is the mysterious nbytes of its "span". I want my UBound back.
1. What are the LBound, UBound and Count values for global MyArray[50] if global Indexbase is 1?
2. Same, Indexbase 0?
3. Same, for MyArray[50] defined globally with global Indexbases 1 and 0 but accessed within a function where its local Indexbase is, say, 100?
Why all this trickery instead of simle MyArray[-24 To 25], LBound, UBound, and Count?
Painfully yours, :-\
-
Yes, O2 arrays are raw!
For static arrays we now have countof for the number of elements
If you set the indexbase to 2 then your subsequent LBOUND for all arrays will be 2 etc.
The indexbase is not associated with, or recorded for any particular array, but it is scoped.
The UBOUND will be LBOUND(a)+countof(a)-1
When passing an array (byref) to a function, you will need to pass the countof() as an extra parameter.
-
But I am not dealing with static arrays in my OxyLISP! All I have are overlays, and I don't even know what they are pointing to at any given point in time because the GC swaps them transparently!
-
Hi Mike,
Here is a set of macros for managing dynamic arrays of any flat type. by casting dynamic arrays as bstrings, they can be pointer-swapped, copied, resized, appended, inserted and deleted, and anything else you can do with a string. The underlying olestring, has a record of its byte length so the element count can be derived.
I think some of it, at least, might be useful for OxyLisp.
The key to these macros is
def DynRef cast bstring @
dynamic.inc
'DYNAMIC ARRAYS - FLAT TYPES ONLY
def Dynamic 'type varname
=========================
%1 * %2
end def
def DynRef cast bstring @
=========================
def DynDim 'type varname(quantity)
==================================
dim %1 * %2
@%2=getmemory sizeof(%1)*
end def
'not for arrays of strings & bstrings
'flat types only
def DynClone 'clone = original
==============================
typeof %3 * %1
DynRef %1 = DynRef %3
end def
def DynRedim 'varname(quantity)
===============================
scope
sys q1=len DynRef %1
sys q2=sizeof(%1) * %2
if q2>q1 then
DynRef %1 += nuls q2-q1
elseif q2<q1 then
DynRef %1 = left DynRef %1,q2
end if
end scope
end def
def DynExpand 'varname(quantity)
================================
scope
sys q1 = len(DynRef %1)
sys q2 = (%2)*sizeof(%1)
if q2>q1 then
DynRef %1 +=nuls q2-q1
end if
end scope
end def
macro DynGetFile(n,d) 'filename, array
======================================
DynRef d=GetFile n
end macro
macro DynPutFile(n,d) 'filename, array
======================================
putfile n,DynRef d
end macro
macro DynBytes(v) 'return byte count
====================================
len(DynRef v)
end macro
macro DynCount(v) 'return element count
=======================================
len(DynRef v)/sizeof(v)
end macro
macro DynAppend(v,w)
====================
DynRef v += DynRef w
macro DynInsert(v,w,i) 'String,InsString,elOffset
=================================================
DynRef v=left(DynRef v,i*sizeof(v)) &
DynRef w &
mid(DynRef v,1+i*sizeof(v))
end macro
macro DynDelete(v,i,n) 'String, elOffset, n to delete
=====================================================
DynRef v=left(DynRef v,i*sizeof(v)) &
mid(DynRef v,1+(i+n)*sizeof(v))
end macro
macro DynSwap(a,b) 'array a, array b
====================================
scope
sys _a=@a
@a=@b
@b=_a
end scope
end macro
macro DynFree(v) 'array
=======================
freememory @v
@v=0
end macro
EXAMPLES
dynamic1
includepath "$/inc/"
include "dynamic.inc"
include "console.inc"
indexbase 0
sys nx=100,ny=50
DynDim double d (nx*ny)
d(50+10*nx)=123
DynClone e = d
DynRedim e(nx*ny/2)
'print recordof d
print "array d" cr
print "value " & str(d(50+10*nx)) & cr
print "count " & str(DynCount d) & cr
print "bytes " & str(DynBytes d) & cr
print "array e" cr
print "value " & str(e(50+10*nx)) & cr
print "count " & str(DynCount e) & cr
print "bytes " & str(DynBytes e) & cr
Waitkey
DynFree d
DynFree e
Dynamic2
includepath "$/inc/"
include "dynamic.inc"
include "console.inc"
indexbase 0
DynDim sys a(1000)
a[2]=123
DynExpand a(2000)
Dynputfile "t.txt", a
printl DynBytes a
printl DynCount a
sys*b
DynGetFile "t.txt", b
printl DynCount b
DynInsert a,b,100
printl DynCount a
DynDelete a,10,5
printl DynCount a
printl a[2]
printl a[97] 'a[102-5]
waitkey
putfile "t.txt"," "
DynFree a
DynFree b
-
Dynamic arrays of strings can also be handled by those macros which do not attempt to duplicate the string references, which might cause cross-linkage problems.
dynamic3
'CONSTRAINED USE FOR DYNAMIC STRING ARRAYS
includepath "$/inc/"
include "dynamic.inc"
include "console.inc"
indexbase 0
DynDim string a(1000)
DynDim string b(100)
DynDim string c(500)
a[20]="abc"
printl a[20]
DynRedim a(500)
DynExpand a(2000)
printl DynCount a
DynInsert a,b,10
DynSwap a,c
printl c[120]
DynSwap a,c
printl a[120]
waitkey
DynFree a
DynFree b
DynFree c
-
Hi Charles,
That's what I call a system approach to the problem! :D
Thanks a lot for this set of oxy-wonders. When OxyLISP is debugged and stable, I will use these to re-implement it based on such dynamic arrays.
Currently OxyLISP uses a set of 50000/100000/200000-element integer and double arrays for its symbol and var table, stack, and heap, respectively, which brings its memory footprint on the order of some 18MB. This design decision was imposed by the slowliness of SB's dynamic arrays and my own uncertainty as to whether O2 has any form of usable dynamic arrays or not. FBLisp could use its own lightning fast linked-list based dynamic arrays and its footprint grew only as large as the largest Scheme program run so far required. But in order to maintain compatibility between the three implementations, I stuck to static arrays.
In fact, that's so promising that I already almost forgot my LBound, UBound, and Count griefs. :)
Also while I'm at it. There are some problems I encounter when using Oxygen #defines: i) I seem to not be able to use previously #defined expressions in the bodies of subsequent #defines, and ii) while a #define allows me to use colons to denote multiline macros, I can't put several pre-#defined expressions on the same script line delimiting them with colons. OxyBASIC uses hundreds of macros throughout its code and these restrictions are making iit almost twice as long as it should be. Can something be done about it, do you think?
-
Mike,
Macro invocations inside macros are resolved late. Could this be your problem?
You can use '\' to denote line feeds in a #define
Alternatively use macro ... end macro.
simple equate expressions are best done using '%' or '$'
#define a(n) n
#define b a(1) + \
a(2)
macro c
a(1) +
a(2)
end macro
print b : print c
-
Alas Charles,
This:
#DEFINE FOREGROUND_BLUE &H1
#DEFINE FOREGROUND_GREEN &H2
#DEFINE FOREGROUND_RED &H4
#DEFINE COLOR_WHT SetConsoleTextAttribute(ConsOut, FOREGROUND_RED OR FOREGROUND_GREEN OR FOREGROUND_BLUE)
and this:
#DEFINE B_to_P ptype = btype: pvalue = bvalue
#DEFINE C_to_P ptype = ctype: pvalue = cvalue
B_to_P: C_to_P
doesn't work for me. Whenever I try to use such a COLOR_WHT, it is reported with "variable COLOR_WHT undefined", and whenever I write B_to_P: C_to_P on the same line, the low-level asm emits a message I can't fully comprehend.
While I can subside to using numeric color attributes in the former macros which clears the error, I need to use the lines formatted in a B_to_P: C_to_P style in hundreds of places throughout the code, but I can't. I have to split the macro pairs, triplets and quadruplets into separate lines, which impairs code readability greately and makes the script unbearably long.
-
Mike,
I can get both examples working correctly. So I wonder if the definitions have been trapped inside a scope. ie: inside a procedure or between round brackets:
(
...
)
by specifying #preprocess at the top of your main code, scopes have no effect on #defines
Test code:
'#preprocess
'(
#DEFINE FOREGROUND_BLUE &H1
#DEFINE FOREGROUND_GREEN &H2
#DEFINE FOREGROUND_RED &H4
#DEFINE COLOR_WHT SetConsoleTextAttribute(ConsOut, FOREGROUND_RED OR FOREGROUND_GREEN OR FOREGROUND_BLUE)
#DEFINE B_to_P ptype = btype: pvalue = bvalue
#DEFINE C_to_P ptype = ctype: pvalue = cvalue
')
function SetConsoleTextAttribute(sys a,b)
end function
sys ptype, btype, ctype, pvalue, bvalue, cvalue
sys consout
#show COLOR_WHT
#show B_to_P
#show C_to_P
-
Charles,
These and others are global #defines grouped at the top of the script immediately following includepath "$\inc\", include "console.inc", indexbase 0, 3 forward function declarations, and your refswap macro.
I'll try your #preprocess suggestion and will report here later.
-
Charles,
Regretfully #preprocess doesn't cure the problem. Please consider the attached snapshot where
Car: R_to_Q: C_to_P
are three consecutive macros delimited with colons following the rules of conventional punctuation. The preprocessor refuses to regard each of them as a macro and throws an exception as shown in the snapshot after the first occurrence.
But if I change the punctuation breaking its rules as follows:
Car : R_to_Q : C_to_P
the preprocessor swallows my hack and the program launches for execution.
Probably the PP tends to regard Car: and other similar occurrences as labels rather than macros. I think this is an evident bug of the PP parser that should be fixed by all means.
.
-
Got it! Thought it was a label - didn't check for other metatypes.
http://www.oxygenbasic.org/o2zips/Oxygen.zip
-
Bingo, Charles! Now everything works perfectly for me. :)
Thanks for your invaluable help!
-
Mike,
Do you see continued life with the LISP in BASIC Scheme version as a prototyping tool?
I have to admit seeing it run in O2 makes it worth investing in.
-
Well John,
Despite its terrific speed, OxyLISP remains a Scheme interpreter while it's a Scheme JIT compiler for FBSL that I'm in fact after. The compiler is going to be yet 5 to 10 times faster than OxyLISP even if its internal structure is going to be much more complex than that of OxyLISP.
But the original Lisp-in-Basic was clearly written by a Lisper, not a BASICer or Ceer. Its BASIC structure reimplements how this Lisp-in-Basic would look if it rather were a Lisp-in-Lisp program. This is interesting for me because what I'm currently doing with my Scheme JIT compiler is the translation of Abdoulaziz Ghuloum's code originally written in Petite Chez Scheme, to C. So Lisp-in-Basic serves as a prototype providing me with ready-made solutions of how to mimic LISP constructs and code patterns in a functional language such as C.
On the other hand, I've put so much of my effort into the BL project that I'm currently feeling pretty much fed up with it. Yet it is so rewarding to see how fast OxyLISP turns out to be that I'm also feeling myself obliged to bring it to completion no matter what. :)
-
On the other hand, I've put so much of my effort into the BL project that I'm currently feeling pretty much fed up with it. Yet it is so rewarding to see how fast OxyLISP turns out to be that I'm also feeling myself obliged to bring it to completion no matter what.
Sort of like looking into the mirror and calling yourself ugly. ;D
-
Yeah, sort of a love-hate relationship. :D