Author Topic: Why Don't We?  (Read 7037 times)

0 Members and 1 Guest are viewing this topic.

Mike Lobanovsky

  • Guest
Why Don't We?
« 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:
Quote from: CHM
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, :-\

Charles Pegge

  • Guest
Re: Why Don't We?
« Reply #1 on: September 18, 2014, 01:56:22 PM »
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.

Mike Lobanovsky

  • Guest
Re: Why Don't We?
« Reply #2 on: September 18, 2014, 02:31:45 PM »
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!

Charles Pegge

  • Guest
Re: Why Don't We?
« Reply #3 on: September 19, 2014, 02:12:44 AM »
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
Code: [Select]
'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
Code: [Select]
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
Code: [Select]
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

Charles Pegge

  • Guest
Re: Why Don't We?
« Reply #4 on: September 19, 2014, 03:03:38 AM »
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
Code: [Select]
'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

Mike Lobanovsky

  • Guest
Re: Why Don't We?
« Reply #5 on: September 19, 2014, 03:10:14 AM »
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?

Charles Pegge

  • Guest
Re: Why Don't We?
« Reply #6 on: September 19, 2014, 03:51:46 AM »
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 '$'


Code: [Select]
#define a(n) n
#define b a(1) + \
          a(2)
macro c
a(1) +
a(2)
end macro

print b : print c

Mike Lobanovsky

  • Guest
Re: Why Don't We?
« Reply #7 on: September 19, 2014, 04:19:20 AM »
Alas Charles,

This:
Code: [Select]
#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:
Code: [Select]
#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.
« Last Edit: September 19, 2014, 04:27:57 AM by Mike Lobanovsky »

Charles Pegge

  • Guest
Re: Why Don't We?
« Reply #8 on: September 19, 2014, 06:54:11 AM »
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:
Code: [Select]
'#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

Mike Lobanovsky

  • Guest
Re: Why Don't We?
« Reply #9 on: September 19, 2014, 07:50:21 AM »
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.

Mike Lobanovsky

  • Guest
Re: Why Don't We?
« Reply #10 on: September 19, 2014, 11:40:21 AM »
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.


.

Charles Pegge

  • Guest
Re: Why Don't We?
« Reply #11 on: September 19, 2014, 01:00:17 PM »
Got it! Thought it was a label - didn't check for other metatypes.

http://www.oxygenbasic.org/o2zips/Oxygen.zip

Mike Lobanovsky

  • Guest
Re: Why Don't We?
« Reply #12 on: September 19, 2014, 01:42:05 PM »
Bingo, Charles! Now everything works perfectly for me. :)

Thanks for your invaluable help!

JRS

  • Guest
Re: Why Don't We?
« Reply #13 on: September 19, 2014, 07:07:27 PM »
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.




Mike Lobanovsky

  • Guest
Re: Why Don't We?
« Reply #14 on: September 20, 2014, 12:04:06 AM »
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. :)