Skip to content

Commit

Permalink
Merge pull request #574 from a740g/iif-support
Browse files Browse the repository at this point in the history
Implement _IIF and fix _ROR & _ROL
  • Loading branch information
a740g authored Nov 25, 2024
2 parents 59f87a6 + 8865f2c commit 6e6b574
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 67 deletions.
49 changes: 4 additions & 45 deletions internal/c/libqb/include/bitops.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,18 @@ uint64_t getubits(uint32_t bsize, uint8_t *base, intptr_t i);
int64_t getbits(uint32_t bsize, uint8_t *base, intptr_t i);
void setbits(uint32_t bsize, uint8_t *base, intptr_t i, int64_t val);

// The rotation functions below are the way they are for a couple of reasons:
// 1. They are safer (well folks seem to think so; see https://en.wikipedia.org/wiki/Circular_shift#Implementing_circular_shifts)
// 2. We are using C library constants and there is just 1 numeric literal - '1'
// 3. GGC recognizes the 'pattern' and will optimize it out to 'roX' and 3 more instructions when using O2
static inline constexpr uint8_t func__rol8(uint8_t value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(value) - 1;
template <typename T> static inline constexpr T func__rol(T value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(T) - 1;
count &= mask;
return (value << count) | (value >> (-count & mask));
}

static inline constexpr uint8_t func__ror8(uint8_t value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(value) - 1;
template <typename T> static inline constexpr T func__ror(T value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(T) - 1;
count &= mask;
return (value >> count) | (value << (-count & mask));
}

static inline constexpr uint16_t func__rol16(uint16_t value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(value) - 1;
count &= mask;
return (value << count) | (value >> (-count & mask));
}

static inline constexpr uint16_t func__ror16(uint16_t value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(value) - 1;
count &= mask;
return (value >> count) | (value << (-count & mask));
}

static inline constexpr uint32_t func__rol32(uint32_t value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(value) - 1;
count &= mask;
return (value << count) | (value >> (-count & mask));
}

static inline constexpr uint32_t func__ror32(uint32_t value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(value) - 1;
count &= mask;
return (value >> count) | (value << (-count & mask));
}

static inline constexpr uint64_t func__rol64(uint64_t value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(value) - 1;
count &= mask;
return (value << count) | (value >> (-count & mask));
}

static inline constexpr uint64_t func__ror64(uint64_t value, unsigned int count) {
const unsigned int mask = CHAR_BIT * sizeof(value) - 1;
count &= mask;
return (value >> count) | (value << (-count & mask));
}

// bit-shifting
static inline constexpr uint64_t func__shl(uint64_t a1, int b1) {
return a1 << b1;
}
Expand Down
80 changes: 59 additions & 21 deletions source/qb64pe.bas
Original file line number Diff line number Diff line change
Expand Up @@ -16676,41 +16676,85 @@ FUNCTION evaluatefunc$ (a2$, args AS LONG, typ AS LONG)
IF Error_Happened THEN EXIT FUNCTION
'------------------------------------------------------------------------------------------------------------

' IIF support
IF n$ = "_IIF" THEN
IF curarg = 1 THEN ' expression
r$ = r$ + "("
nocomma = 1
ELSEIF curarg = 2 THEN ' true part
IF sourcetyp AND ISREFERENCE THEN e$ = refer(e$, sourcetyp, 0)
IF Error_Happened THEN EXIT FUNCTION

typ& = sourcetyp ' return type is always derived from true part
r$ = r$ + ")?(" + e$ + "):"
e$ = ""
nocomma = 1

GOTO dontevaluate
ELSEIF curarg = 3 THEN ' false part
nocomma = 0

IF (sourcetyp AND ISSTRING) <> (typ& AND ISSTRING) THEN
Give_Error "falsePart and truePart must be of the same type"
EXIT FUNCTION
END IF

IF sourcetyp AND ISREFERENCE THEN e$ = refer(e$, sourcetyp, 0)
IF Error_Happened THEN EXIT FUNCTION

r$ = r$ + "(" + e$ + "))"

GOTO evalfuncspecial
END IF
END IF

' ROR & ROL support
IF n$ = "_ROR" OR n$ = "_ROL" THEN
rotlr_n$ = LCASE$(RIGHT$(n$, 3)) ' Get the last 3 characters and convert to lower case. We'll need this to construct the C call
IF curarg = 1 THEN ' First parameter
IF (sourcetyp AND ISSTRING) OR (sourcetyp AND ISFLOAT) OR (sourcetyp AND ISOFFSET) OR (sourcetyp AND ISUDT) THEN ' Bad parameters types
IF n$ = "_ROR" _ORELSE n$ = "_ROL" THEN
rotlr_n$ = LCASE$(RIGHT$(n$, 3)) ' we'll need this to construct the C call

IF curarg = 1 THEN ' first parameter
IF (sourcetyp AND ISSTRING) _ORELSE (sourcetyp AND ISFLOAT) THEN
Give_Error "Expected non-floating-point value"
EXIT FUNCTION
END IF
IF sourcetyp AND ISREFERENCE THEN e$ = refer(e$, sourcetyp, 0) ' This gets the C-style dereferencing syntax for an identifier (I think XD)

IF sourcetyp AND ISREFERENCE THEN e$ = refer(e$, sourcetyp, 0)
IF Error_Happened THEN EXIT FUNCTION

' Establish which function (if any!) should be used
IF (sourcetyp AND 511) = 8 THEN ' sourcetyp is the type of data (bits can be examined to get more details)
e$ = "func__" + rotlr_n$ + "8(" + e$
typ& = UBYTETYPE - ISPOINTER ' We force the return type here. This is passed back up to the caller
e$ = "func__" + rotlr_n$ + "<uint8_t>(" + e$
typ& = UBYTETYPE - ISPOINTER ' the return type is passed back up to the caller
ELSEIF (sourcetyp AND 511) = 16 THEN
e$ = "func__" + rotlr_n$ + "16(" + e$
e$ = "func__" + rotlr_n$ + "<uint16_t>(" + e$
typ& = UINTEGERTYPE - ISPOINTER
ELSEIF (sourcetyp AND 511) = 32 THEN
e$ = "func__" + rotlr_n$ + "32(" + e$
e$ = "func__" + rotlr_n$ + "<uint32_t>(" + e$
typ& = ULONGTYPE - ISPOINTER
ELSEIF (sourcetyp AND 511) = 64 THEN
e$ = "func__" + rotlr_n$ + "64(" + e$
e$ = "func__" + rotlr_n$ + "<uint64_t>(" + e$
typ& = UINTEGER64TYPE - ISPOINTER
ELSE
Give_Error "Unknown data size"
EXIT FUNCTION
END IF
r$ = e$ ' Save whatever syntax he have so far
e$ = "" ' This must be cleared so that it is not repeated when we get to parameter 2
GOTO dontevaluate ' Don't evaluate until we get the second parameter
ELSEIF curarg = 2 THEN ' Second parameter

r$ = e$ ' save whatever syntax he have so far
e$ = "" ' this must be cleared so that it is not repeated when we get to parameter 2

GOTO dontevaluate ' don't evaluate until we get the second parameter
ELSEIF curarg = 2 THEN ' second parameter
IF (sourcetyp AND ISSTRING) _ORELSE (sourcetyp AND ISFLOAT) THEN
Give_Error "Expected non-floating-point value"
EXIT FUNCTION
END IF

IF sourcetyp AND ISREFERENCE THEN e$ = refer(e$, sourcetyp, 0)
IF Error_Happened THEN EXIT FUNCTION

r$ = r$ + e$ + ")"
GOTO evalfuncspecial ' Evaluate now that we have everything

GOTO evalfuncspecial ' evaluate now that we have everything
END IF
END IF

Expand Down Expand Up @@ -23690,15 +23734,9 @@ FUNCTION SCase2$ (t$)
CASE "_ANDALSO"
SCase2$ = "_AndAlso"

CASE "ANDALSO"
SCase2$ = "AndAlso"

CASE "_ORELSE"
SCase2$ = "_OrElse"

CASE "ORELSE"
SCase2$ = "OrElse"

CASE ELSE
newWord = -1
temp$ = ""
Expand Down
10 changes: 10 additions & 0 deletions source/subs_functions/subs_functions.bas
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ SUB reginternal
clearid
id.n = "_Continue": id.subfunc = 2: id.callname = "sub_stub": regid
clearid
id.n = "_IIf"
id.subfunc = 1
id.args = 3
id.arg = MKL$(OFFSETTYPE - ISPOINTER) + MKL$(STRINGTYPE - ISPOINTER) + MKL$(STRINGTYPE - ISPOINTER) ' overridden in qb64pe.bas
id.specialformat = "?,?,?"
id.ret = STRINGTYPE - ISPOINTER ' overridden in qb64pe.bas
id.hr_syntax = "_IIF(expression, truePart, falsePart)"
regid
clearid
id.n = "_Resize"
id.subfunc = 2
Expand Down
2 changes: 1 addition & 1 deletion source/subs_functions/syntax_highlighter_list.bas
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ listOfKeywords$ = listOfKeywords$ +_
' [I] - Keywords alphabetical (1st line = QB64, 2nd line = QB4.5, 3rd line = OpenGL)
listOfKeywords$ = listOfKeywords$ +_
"_ICON@_INCLERRORFILE$@_INCLERRORLINE@_INFLATE$@_INPUTBOX$@_INSTRREV@_INTEGER64@" +_
"_ICON@_IIF@_INCLERRORFILE$@_INCLERRORLINE@_INFLATE$@_INPUTBOX$@_INSTRREV@_INTEGER64@" +_
"IF@IMP@INKEY$@INP@INPUT@INPUT$@INSTR@INT@INTEGER@INTERRUPT@INTERRUPTX@IOCTL@IOCTL$@IS@" +_
"_GLINDEXD@_GLINDEXDV@_GLINDEXF@_GLINDEXFV@_GLINDEXI@_GLINDEXIV@_GLINDEXMASK@_GLINDEXPOINTER@_GLINDEXS@_GLINDEXSV@_GLINDEXUB@_GLINDEXUBV@_GLINITNAMES@_GLINTERLEAVEDARRAYS@_GLISENABLED@_GLISLIST@_GLISTEXTURE@"
Expand Down
60 changes: 60 additions & 0 deletions tests/compile_tests/iif/iif_test.bas
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
$CONSOLE:ONLY

OPTION _EXPLICIT

CONST TEXT_HELLO = "hello"
CONST TEXT_WORLD = "world"

TYPE t
f AS SINGLE
i AS _OFFSET
END TYPE

DIM f AS SINGLE: f = 3.141592653589793239
DIM i AS _OFFSET: i = 255
DIM s AS STRING: s = TEXT_HELLO

DIM t AS t
t.f = f
t.i = i

DIM a(1) AS SINGLE
a(0) = f
a(1) = _IIF(_FALSE, 0, i) ' naughty!

PRINT _IIF(f > 0, f, i)
PRINT _IIF(i > 0, i, f)

PRINT _IIF(t.f > 0, t.f, t.i)
PRINT _IIF(t.i > 0, t.i, t.f)

PRINT _IIF(a(0) > 0, a(0), a(1))
PRINT _IIF(a(1) > 0, a(1), a(0))

PRINT _IIF(f > 0, 1.1!, 2)
PRINT _IIF(i > 0, 2, 1.1!)

PRINT _IIF(f = 0, "f", "i")
PRINT _IIF(i = 0, "i", "f")

' PRINT and LPRINT has bugs and throws error when using string comparisons
WRITE _IIF(s = TEXT_HELLO, TEXT_HELLO, TEXT_WORLD)
WRITE _IIF(s = TEXT_WORLD, 1, 2)

PRINT _IIF(i > 0, foo, bar)
PRINT _IIF(i < 0, bar, foo)

DIM age AS _BYTE: age = 17
PRINT _IIF(age >= 18, "over 18", "under 18")

SYSTEM

FUNCTION foo&
PRINT "foo called!"
foo = 512
END FUNCTION

FUNCTION bar&
PRINT "bar called!"
bar = 128
END FUNCTION
17 changes: 17 additions & 0 deletions tests/compile_tests/iif/iif_test.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
3.141593
255
3.141593
255
3.141593
255
1.1
2
i
f
"hello"
2
foo called!
512
foo called!
512
under 18

0 comments on commit 6e6b574

Please sign in to comment.