05-13-2022, 09:55 AM
So here is the BI (CHARSET.BI) file with the DECLARE statements removed -
Having removed all those DECLAREs, I have moved the comments into the BAS (CHARSET.BAS) file. This means that it is now 360+ lines long now -
And finally the demo/test program (TESTSETS.BAS) -
One thing I have noticed is that constants only seem to be valid in the file in which they occur because with the way the code above is I get an invalid declaration in CHARSET.BAS but if I move the CONST line out of CHARSET.BI into CHARSET.BAS above the first CONST in that file, the problem goes away. Is it supposed to be like that or is it a bug?
TR
Code: (Select All)
REM ******************************************************
REM * Filespec : charset.bas charset.bi *
REM * Date : June 23 1997 *
REM * Time : 12:01 *
REM * Revision : 1.0B *
REM * Update : *
REM ******************************************************
REM * Released to the Public Domain *
REM ******************************************************
CONST SetSize = 32 ' Number of bytes to store set contents
TYPE CharSet
MySet AS STRING * SetSize
MySize AS INTEGER
END TYPE
Having removed all those DECLAREs, I have moved the comments into the BAS (CHARSET.BAS) file. This means that it is now 360+ lines long now -
Code: (Select All)
'$INCLUDE: 'CHARSET.BI'
CONST Last = SetSize - 1
CONST TRUE = -1
CONST FALSE = 0
REM ******************************************************
REM * Private SUB - Do not call directly *
REM ******************************************************
SUB SetError ( ErrMessage AS STRING )
PRINT "ERROR : ";ErrMessage
PRINT "ABORTING NOW!"
STOP
END SUB
REM ******************************************************
REM * Private SUB - Do not call directly *
REM ******************************************************
SUB LoadSet ( A AS CharSet, LoadChars AS STRING )
IF LoadChars <> "" THEN
LoadSize% = LEN(LoadChars)
Here% = 1
LocationNext% = INSTR(Here%, LoadChars, "...")
DO WHILE LocationNext% > 0
Start% = ASC(MID$(LoadChars, LocationNext% - 1, 1))
Fini% = ASC(MID$(LoadChars, LocationNext% + 3, 1))
Here% = LocationNext% + 4
IF Start% > Fini% THEN
Start% = (Start% XOR Fini%)
Fini% = (Start% XOR Fini%)
Start% = (Start% XOR Fini%)
END IF
FOR X% = Start% TO Fini%
Y% = 1 + (X% \ 8)
Z% = X% MOD 8
MID$(A.MySet, Y%, 1) = CHR$(ASC(MID$(A.Myset, Y%, 1)) OR PowerOf2%(Z%))
NEXT X%
IF Here% >= LoadSize% THEN
EXIT DO
END IF
LocationNext% = INSTR(Here%, LoadChars, "...")
LOOP
IF Here% < LoadSize% THEN
FOR X% = Here% TO LoadSize%
AChar$ = MID$(LoadChars, X%, 1)
Y% = 1 + (ASC(AChar$) \ 8)
Z% = ASC(AChar$) MOD 8
MID$(A.MySet, Y%, 1) = CHR$(ASC(MID$(A.MySet, Y%, 1)) OR PowerOf2%(Z%))
NEXT X%
END IF
END IF
END SUB
REM ******************************************************
REM * Private SUB - Do not call directly *
REM ******************************************************
SUB Recount ( A AS CharSet )
A.MySize = 0
FOR X% = 1 TO SetSize
TestChar$ = MID$(A.MySet, X%, 1)
FOR Y% = 0 TO 7
IF (ASC(TestChar$) AND PowerOf2%(Y%)) <> 0 THEN
A.MySize = A.MySize + 1
END IF
NEXT Y%
NEXT X%
END SUB
REM ******************************************************
REM * Private FUNCTION - Do not call directly *
REM ******************************************************
FUNCTION PowerOf2% ( Power AS INTEGER )
SELECT CASE Power
CASE 0
PowerOf2% = 1
CASE 1
PowerOf2% = 2
CASE 2
PowerOf2% = 4
CASE 3
PowerOf2% = 8
CASE 4
PowerOf2% = 16
CASE 5
PowerOf2% = 32
CASE 6
PowerOf2% = 64
CASE 7
PowerOf2% = 128
CASE ELSE
PowerOf2% = 0
END SELECT
END FUNCTION
REM *****************************************************************
REM * Must be called before a charset is used unless that charset *
REM * is used to hold the results of a set operation. Valid set *
REM * operations that can be called in lieu of this routine are - *
REM * CopySet, MakeSetEmpty, SetComplement, SetUnion, SetDifference,*
REM * SetIntersection and SymmetricSetDifference, where without *
REM * exception the uninitialised set would be used for the *
REM * rightmost parameter. *
REM * *
REM * The string InitialChars is used to specify the initial *
REM * contents of the CharSet being initialised. The format of *
REM * the string is as follows. *
REM * *
REM * If an empty set is desired it is possible to pass an empty *
REM * string "" to this routine, although the routine MakeSetEmpty *
REM * would probably be quicker. *
REM * *
REM * A range of characters can be specified by the use of a *
REM * trigraph (...) in the form a...z which would tell this *
REM * routine to include all the characters from lower case 'a' to *
REM * lower case 'z' inclusive. More than one range of characters *
REM * may be specified for a set, but all ranges MUST be the first *
REM * of the characters specified. *
REM * *
REM * A list of the actual characters required to be contained *
REM * within the set such as "axwf9\" may be part of (or the whole *
REM * of) the string, but MUST appear after any range(s) of *
REM * characters. *
REM * *
REM * See the example program for more help. *
REM *****************************************************************
SUB InitialiseSet ( A AS CharSet, InitialChars AS STRING )
MakeSetEmpty A
LoadSet A, InitialChars
Recount A
END SUB
REM *****************************************************************
REM * Copies the contents of one set to another. *
REM *****************************************************************
SUB CopySet ( This AS CharSet, ToThis AS CharSet )
ToThis.MySet = This.MySet
ToThis.MySize = This.Mysize
END SUB
REM *****************************************************************
REM * Adds the characters of the string IncludeChars to set A. The *
REM * same rules for the contents of the string used by the routine *
REM * InitialiseSet apply. *
REM *****************************************************************
SUB IncludeInSet ( A AS CharSet, IncludeChars AS STRING )
LoadSet A, IncludeChars
Recount A
END SUB
REM *****************************************************************
REM * If any of the characters in ExcludedChars are also part of *
REM * set A, such characters will be removed from set A. *
REM *****************************************************************
SUB ExcludeFromSet ( A AS CharSet, ExcludedChars AS STRING )
DIM TempSet AS CharSet
IF ExcludedChars = "" THEN
SetError("ExcludeFromSet - No chars to exclude!")
END IF
InitialiseSet TempSet, ExcludedChars
FOR X% = 1 TO SetSize
MID$(A.MySet, X%, 1) = CHR$(ASC(MID$(A.MySet, X%, 1)) AND NOT(ASC(MID$(TempSet.MySet, X%, 1))))
NEXT X%
Recount A
END SUB
REM *****************************************************************
REM * Returns the number of elements of set A. *
REM *****************************************************************
FUNCTION Cardinality% ( A AS CharSet )
Cardinality% = A.MySize
END FUNCTION
REM *****************************************************************
REM * Tests for set A being empty. *
REM *****************************************************************
FUNCTION SetIsEmpty ( A AS CharSet )
SetIsEmpty = (A.MySize = 0)
END FUNCTION
REM *****************************************************************
REM * Empties set A. *
REM *****************************************************************
SUB MakeSetEmpty ( A AS CharSet )
FOR X% = 1 TO SetSize
MID$(A.MySet, X%, 1) = CHR$(0)
NEXT X%
A.MySize = 0
END SUB
REM *****************************************************************
REM * In order to use this routine, the string TestChar MUST only *
REM * have a single character. If the string is empty or it has *
REM * more than 1 character, an error message will be displayed and *
REM * the program will be STOPped. *
REM * *
REM * This routine tests whether the character TestChar is a member *
REM * of set A (TestChar IN A). *
REM *****************************************************************
FUNCTION IsMember ( A AS CharSet, TestChar AS STRING )
IF TestChar = "" THEN
SetError("IsMember - No char to test!")
END IF
IF LEN(TestChar) > 1 THEN
SetError("IsMember - Too many chars to test!")
END IF
Y% = 1 + (ASC(TestChar) \ 8)
Z% = ASC(TestChar) MOD 8
IsMember = ((ASC(MID$(A.MySet, Y%, 1)) AND PowerOf2(Z%)) <> 0)
END FUNCTION
REM *****************************************************************
REM * Converts the set A to the string OutChars for PRINTING etc. *
REM *****************************************************************
SUB GetSetContents ( A AS CharSet, OutChars AS String )
OutChars = ""
FOR X% = 1 TO SetSize
Temp$ = MID$(A.MySet, X%, 1)
FOR Y% = 0 TO 7
IF (ASC(Temp$) AND PowerOf2%(Y%)) <> 0 THEN
OutChars = OutChars + CHR$(((X% - 1) * 8) + Y%)
END IF
NEXT Y%
NEXT X%
END SUB
REM *****************************************************************
REM * Tests for A = B. *
REM *****************************************************************
FUNCTION SetEquality ( A AS CharSet, B AS CharSet )
SetEquality = (A.MySet = B.MySet)
END FUNCTION
REM *****************************************************************
REM * Tests for A <> B. *
REM *****************************************************************
FUNCTION SetInequality ( A AS CharSet, B AS CharSet )
SetInequality = (A.MySet <> B.MySet)
END FUNCTION
REM *****************************************************************
REM * Tests to see if all of the characters contained in the set *
REM * This are also present in the set OfThis, i.e that the set *
REM * This is a subset of the set OfThis. Note if the 2 sets are *
REM * equal or the set This is empty then the set This IS a subset *
REM * of the set OfThis. *
REM *****************************************************************
FUNCTION IsSubsetOf ( This AS CharSet, OfThis AS CharSet )
IF SetEquality(This, OfThis) THEN
IsSubsetOf = TRUE
EXIT FUNCTION
END IF
IF SetIsEmpty(This) THEN
IsSubsetOf = TRUE
EXIT FUNCTION
END IF
IF This.MySize > OfThis.MySize THEN
IsSubsetOf = FALSE
EXIT FUNCTION
END IF
FOR X% = 1 TO SetSize
TestChar1$ = MID$(This.MySet, X%, 1)
TestChar2$ = MID$(OfThis.MySet, X%, 1)
FOR Y% = 0 TO 7
Z% = PowerOf2%(Y%)
P% = (ASC(TestChar1$) AND Z%)
Q% = (ASC(TestChar2$) AND Z%)
IF ((P% <> 0) AND (Q% = 0)) THEN
IsSubsetOf = FALSE
EXIT FUNCTION
END IF
NEXT Y%
NEXT X%
IsSubsetOf = TRUE
END FUNCTION
REM *****************************************************************
REM * Identical to the routine IsSubsetOf with the exception that *
REM * the 2 sets may not be equal (This <> OfThis). *
REM *****************************************************************
FUNCTION IsStrictSubsetOf ( This AS CharSet, OfThis AS CharSet )
IF SetEquality(This, OfThis) THEN
IsStrictSubsetOf = FALSE
EXIT FUNCTION
END IF
IsStrictSubsetOf = IsSubsetOf(This, OfThis)
END FUNCTION
REM *****************************************************************
REM * The operation set complement places all the characters that *
REM * are NOT part of the set A into the set ComplementOfA. *
REM *****************************************************************
SUB SetComplement ( A AS CharSet, ComplementOfA AS CharSet )
FOR X% = 1 TO SetSize
MID$(ComplementOfA.MySet, X%, 1) = CHR$((ASC(MID$(A.MySet, X%, 1))) XOR 255)
NEXT X%
Recount ComplementOfA
END SUB
REM *****************************************************************
REM * The operation set union combines all the characters that are *
REM * in set A and all the characters that are in set B and returns *
REM * the result of this in set C. *
REM *****************************************************************
SUB SetUnion ( A AS CharSet, B AS CharSet, C AS CharSet )
FOR X% = 1 TO SetSize
MID$(C.MySet, X%, 1) = CHR$(ASC(MID$(A.MySet, X%, 1)) OR ASC(MID$(B.MySet, X%, 1)))
NEXT X%
Recount C
END SUB
REM *****************************************************************
REM * The operation set difference places only those characters of *
REM * set A which are NOT part of set B into set C. *
REM *****************************************************************
SUB SetDifference ( A AS CharSet, B AS CharSet, C AS CharSet )
FOR X% = 1 TO SetSize
MID$(C.MySet, X%, 1) = CHR$(ASC(MID$(A.MySet, X%, 1)) AND NOT(ASC(MID$(B.MySet, X%, 1))))
NEXT X%
Recount C
END SUB
REM *****************************************************************
REM * After this operation set C will contain only those characters *
REM * which occur in both set A and set C. *
REM *****************************************************************
SUB SetIntersection ( A AS CharSet, B AS CharSet, C AS CharSet )
FOR X% = 1 TO SetSize
MID$(C.MySet, X%, 1) = CHR$(ASC(MID$(A.MySet, X%, 1)) AND ASC(MID$(B.MySet, X%, 1)))
NEXT X%
Recount C
END SUB
REM *****************************************************************
REM * This operation is the set equivalent of the logical operation *
REM * exclusive or, in that after this operation set C will contain *
REM * only those characters that occur in either set A or set B but *
REM * not in both. *
REM *****************************************************************
SUB SymmetricSetDifference ( A AS CharSet, B AS CharSet, C AS CharSet )
FOR X% = 1 TO SetSize
MID$(C.MySet, X%, 1) = CHR$(ASC(MID$(A.MySet, X%, 1)) XOR ASC(MID$(B.MySet, X%, 1)))
NEXT X%
Recount C
END SUB
And finally the demo/test program (TESTSETS.BAS) -
Code: (Select All)
'$INCLUDE: 'charset.bi'
DIM ASet AS CharSet, BSet AS CharSet, CSet AS CharSet, DSet AS CharSet
DIM ESet AS CharSet, FSet AS CharSet, GSet AS CharSet, HSet AS CharSet
DIM ISet AS CharSet, JSet AS CharSet, KSet AS CharSet, SetL AS CharSet
DIM MSet AS CharSet, NSet AS CharSet, OSet AS CharSet, SetP AS CharSet
DIM QSet AS CharSet, SetR AS CharSet, SSet AS CharSet, TSet AS CharSet
AStr$ = CHR$(129) + "..." + CHR$(147)
BStr$ = CHR$(0) + "..." + CHR$(31)
CStr$ = CHR$(148) + "..." + CHR$(255)
DStr$ = CHR$(33) + "..." + CHR$(127)
EStr$ = CHR$(0) + "...MO..." + CHR$(255)
CLS
PRINT "Working .";
InitialiseSet ASet, "A...Za...z"
PRINT ".";
InitialiseSet BSet, ",.<>;':@#~{}[]"
PRINT ".";
InitialiseSet CSet, "0...9!œ$%^&*()_-+=\|"
PRINT ".";
InitialiseSet DSet, AStr$
PRINT ".";
InitialiseSet ESet, "a...z0...9!œ$%^&*()_-+=\|;:'@#~[]{}"
PRINT ".";
InitialiseSet FSet, "acegikmoqsuwy"
PRINT ".";
InitialiseSet GSet, "A...Z"
PRINT ".";
InitialiseSet HSet, "a...z"
PRINT ".";
CopySet ASet, ISet
PRINT ".";
InitialiseSet JSet, ""
PRINT ".";
SetComplement JSet, KSet
PRINT ".";
MakeSetEmpty SetL
PRINT ".";
InitialiseSet MSet, DStr$
PRINT ".";
InitialiseSet NSet, EStr$
PRINT ".";
SetComplement NSet, OSet
PRINT ".";
CopySet NSet, SetP
PRINT ".";
ExcludeFromSet NSet, BStr$
PRINT ".";
GetSetContents ASet, FStr$
PRINT "."
PRINT "Set A contains - "; FStr$
GetSetContents BSet, FStr$
PRINT "Set B contains - "; FStr$
GetSetContents CSet, FStr$
PRINT "Set C contains - "; FStr$
GetSetContents DSet, FStr$
PRINT "Set D contains - "; FStr$
GetSetContents ESet, FStr$
PRINT "Set E contains - "; FStr$
GetSetContents FSet, FStr$
PRINT "Set F contains - "; FStr$
GetSetContents GSet, FStr$
PRINT "Set G contains - "; FStr$
GetSetContents HSet, FStr$
PRINT "Set H contains - "; FStr$
GetSetContents ISet, FStr$
PRINT "Set I contains - "; FStr$
GetSetContents JSet, FStr$
PRINT "Set J contains - "; FStr$
ExcludeFromSet KSet, BStr$
GetSetContents KSet, FStr$
IncludeInSet KSet, BStr$
PRINT "Set K contains - "; FStr$
GetSetContents SetL, FStr$
PRINT "Set L contains - "; FStr$
GetSetContents MSet, FStr$
PRINT "Set M contains - "; FStr$
GetSetContents NSet, FStr$
PRINT "Set N contains - "; FStr$
GetSetContents OSet, FStr$
PRINT "Set O contains - "; FStr$
ExcludeFromSet SetP, BStr$
GetSetContents SetP, FStr$
IncludeInSet SetP, BStr$
PRINT "Set P contains - "; FStr$
WaitKey
PRINT
PRINT "Cardinality of A = "; Cardinality%(ASet)
PRINT "Cardinality of B = "; Cardinality%(BSet)
PRINT "Cardinality of C = "; Cardinality%(CSet)
PRINT "Cardinality of D = "; Cardinality%(DSet)
PRINT "Cardinality of E = "; Cardinality%(ESet)
PRINT "Cardinality of F = "; Cardinality%(FSet)
PRINT "Cardinality of G = "; Cardinality%(GSet)
PRINT "Cardinality of H = "; Cardinality%(HSet)
PRINT "Cardinality of I = "; Cardinality%(ISet)
PRINT "Cardinality of J = "; Cardinality%(JSet)
PRINT "Cardinality of K = "; Cardinality%(KSet)
PRINT "Cardinality of L = "; Cardinality%(SetL)
PRINT "Cardinality of M = "; Cardinality%(MSet)
PRINT "Cardinality of N = "; Cardinality%(NSet)
PRINT "Cardinality of O = "; Cardinality%(OSet)
PRINT "Cardinality of P = "; Cardinality%(SetP)
WaitKey
PRINT
IF SetIsEmpty(SetL) THEN
PRINT "Set L is EMPTY!"
ELSE
PRINT "Error in SetIsEmpty!"
STOP
END IF
IF IsMember(HSet, "a") THEN
PRINT "The letter 'a' is a member of set H."
ELSE
PRINT "Error in IsMember!"
STOP
END IF
IF SetEquality(ASet, ISet) THEN
PRINT "Set A = Set I"
ELSE
PRINT "Error in SetEquality!"
STOP
END IF
IF SetInequality(ASet, BSet) THEN
PRINT "Set A <> Set B"
ELSE
PRINT "Error in SetInequality!"
STOP
END IF
IF IsSubsetOf(ISet, ASet) THEN
PRINT "Set I is a subset of Set A"
ELSE
PRINT "Error in IsSubsetOf!"
STOP
END IF
IF NOT (IsStrictSubsetOf(ISet, ASet)) AND IsStrictSubsetOf(FSet, ASet) THEN
PRINT "Set I is NOT a strict subset of A while Set F is."
ELSE
PRINT "Error in IsStrictSubsetOf!"
STOP
END IF
WaitKey
PRINT
PRINT"Testing the operation of set union on -> G + B = Q."
PRINT
GetSetContents GSet, FStr$
PRINT"Set G contains - ";FStr$
GetSetContents BSet, FStr$
PRINT"Set B contains - ";FStr$
SetUnion GSet, BSet, QSet
GetSetContents QSet, FStr$
PRINT"After set union set Q contains - ";FStr$
PRINT
PRINT"Testing the operation of set Difference on -> H - F = R."
PRINT
GetSetContents HSet, FStr$
PRINT"Set H contains - ";FStr$
GetSetContents FSet, FStr$
PRINT"Set F contains - ";FStr$
SetDifference HSet, FSet, SetR
GetSetContents SetR, FStr$
PRINT"After set difference set R contains - ";FStr$
PRINT
PRINT"Testing the operation of set Intersection on -> H * E = S."
PRINT
GetSetContents HSet, FStr$
PRINT"Set H contains - ";FStr$
GetSetContents ESet, FStr$
PRINT"Set E contains - ";FStr$
SetIntersection HSet, ESet, SSet
GetSetContents SSet, FStr$
PRINT"After set intersection set S contains - ";FStr$
PRINT
PRINT"Testing the operation of symmetric set difference on -> C / E = T."
PRINT
GetSetContents CSet, FStr$
PRINT"Set C contains - ";FStr$
GetSetContents ESet, FStr$
PRINT"Set E contains - ";FStr$
SymmetricSetDifference CSet, ESet, TSet
GetSetContents TSet, FStr$
PRINT"After set symmetric set difference set T contains - ";FStr$
PRINT
WaitKey
PRINT"All tests complete."
END
SUB WaitKey
DO
LOOP WHILE INKEY$ = ""
END SUB
'$INCLUDE: 'charset.bas'
One thing I have noticed is that constants only seem to be valid in the file in which they occur because with the way the code above is I get an invalid declaration in CHARSET.BAS but if I move the CONST line out of CHARSET.BI into CHARSET.BAS above the first CONST in that file, the problem goes away. Is it supposed to be like that or is it a bug?
TR