I’m adding this to share my thoughts, especially the quirks that QBJS 0.10.0 has at the time of writing.
Constants
All const
s in QBJS should not have sigil suffixes:
const maxHealth = 100
const vgaWidth = 320
' Valid in QB64 but not in QBJS
const maxHealth% = 100
const vgaWidth& = 320
The interpreter will throw an error if you add a suffix. An example error message is something like this:
ERROR : 0 : Missing initializer in const declaration
This is due to how dynamic typing is implemented in its backend language (JS)
Variables
This of QBJS as a QB64-to-JS transpiler with the full power of JS
This makes dynamic typing possible, which is similar to Lua
Known Issue
Some variables with the system
prefix may cause the transpiler to break
For example, with the name systemFont
being used as a variable:
dim as TBMFont systemFont
This will cause the transpiler to sometimes produce an incomplete object string in JS, but this only happens occasionally
If this happens, just reorder your variable declarations — there may be occasional ordering-dependent bugs in the QBJS-to-JS transpiler
Image Handling
Image handles start at 1000 instead of a negative value
With the same example code
Option _Explicit
Dim As Long imgGasolineMaid
imgGasolineMaid = _LoadImage("IMG\gasoline_maid_256px.png")
Print "image handle:", imgGasolineMaid
_FreeImage imgGasolineMaid
This is the comparison:
QB64: image handle: -10
QBJS: image handle: 1000
Non-existent image
Loading a non-existent image is also possible, which is dangerous:
Option _Explicit
Dim As Long imgInvalid
imgInvalid = _loadImage("invalid_image.jpg")
Print "image handle:", imgInvalid
Output (QBJS):
image handle: 1000
This can be prevented with the _FileExists
statement
Option _Explicit
Dim As Long imgInvalid
If Not _FileExists("invalid_image.jpg") Then
Print "Invalid image doesn't exist!"
Else
imgInvalid = _LoadImage("invalid_image.jpg")
Print "image handle:", imgInvalid
End If
Output:
Invalid image doesn't exist!
Precompiler
$Let
doesn’t work with QBJS
$If
precompiler only works with either:
$If WEB then
$If Javascript then
Use $If WEB
to use the functionalities that only exist in QBJS
Option _Explicit
$if WEB then
import console from "lib\web\console.bas"
console.log "Hello QBJS!"
$else
print "Hello QB64!"
$endif
Use $If Javascript
block to execute any JS code in it:
Option _Explicit
Dim As Long a, b, c
$if javascript then
a = 1
b = 2
c = a + b
$endif
print c
Output:
3
This makes it possible to use fetch API and BigInt
along with QB64
Console / Debugging
$Debug
and any Log
statements (_LogError
, _LogWarn
, _LogTrace
, and so on) don’t work in QBJS
Instead, use the Console import
Option _Explicit
$if web then
import console from "lib\web\console.bas"
console.log "Hello QBJS!"
console.warn "This is a warning"
console.error "This is an error log"
$endif
This makes it possible to use console
in QBJS, but since everything is async
under the hood, using console.log
in Javascript context will throw an error
Function Return Value
Since QBJS uses JavaScript as its backend, it can return an object, or even a UDT
Option _Explicit
Type TCat
name As String
colour As String
End Type
Dim cat As TCat
cat = newCat("Honey", "orange")
Print cat.name, cat.colour
' implementation
Function newCat(name$, colour$)
Dim c As TCat
c.name = name$
c.colour = colour$
newCat = c
End Function
This makes it similar to VBScript but with full browser support
Module System
QBJS is more like the OOP in JS, which makes it also similar to Lua
MAIN.BAS
import console from "lib\web\console.bas"
import YourModule from "modules\hi.bas"
YourModule.sayHi
print YourModule.add(2, 3)
MODULES\HI.BAS
option _explicit
export sayHi, add
sub sayHi
print "Hi from another module!"
end sub
function add(a, b)
add = a + b
end function
JavaScript Interop
(TBA)
Sound Handling
(TBA)
MapTriangle
It’s not possible to use _MapTriangle
statement in QBJS, so drawing a rotated image + scaled image is still a dream at the moment of writing
ByRef or ByVal?
QBJS always uses pass-by-value instead of pass-by-reference like what QB64 uses
Therefore, loadImage
in QBJS returns a value instead of assigning to the target image handle
Boolean value
The values true
& false
can be used immediately from JS
The AND
, OR
, and NOT
operators in QBJS use JS bitwise operators under the hood (Ref: https://qb64phoenix.com/forum/showthread.php?tid=3376)
import console from "lib\web\console.bas"
console.log "What is TRUE? " + i32str(1 > 0)
console.log "What is FALSE? " + i32str(1 < 0)
true
is 1, false
is 0
This makes casting to strings can be easier instead of using the usual STR$
console.log "What is TRUE? " + (1 > 0)
Not
operator in QBJS
The transpiler uses the ~
operator under the hood, which basically flips all the bits in the 64-bit integer. This makes it harder to track a boolean value & an actual integer
So, instead of using the usual
If Not isImageSet(imgHandle) Then Exit Sub
It’s better to use:
If isImageSet(imgHandle) = false Then Exit Sub
But in my case of returning an integer (supposedly), I’ll use an integer return value instead of a boolean. Something like this:
Function isImageSet% (imgHandle As Long)
isImageSet = -(imgHandle >= 1000)
End Function
true
is 1 in JS, while false
is the same: 0
About _PutImage
Compared to QB64, it uses destX + destW - 1
and also destY + destH - 1
because of how the decrement is handled automatically under the hood
So this is the acceptable version in QBJS:
Sub sprRegion (imgHandle As Long, srcX%, srcY%, srcW%, srcH%, destX%, destY%)
_PutImage (destX%, destY%)-(destX% + srcW%, destY% + srcH%), imgHandle, , (srcX%, srcY%)-(srcX% + srcW%, srcY% + srcH%)
End Sub
About copying a UDT
UDT’s (User-defined Types) in QBJS are basically just a JS object
It doesn’t support copying as if it’s a record
in Pascal, since the object reference remains the same
I found this problem when calling loadBMFont, but the output is just ~~~~~~
, while it should be something like "Hello QBJS!"
but with PICO-8 font
So, instead of declaring tempGlyph
at the beginning like what I usually do in Pascal, something like this:
sub loadBMFont ' procedure signature
dim as TBMFontGlyph tempGlyph
' the rest of the implementation
end sub
It’s better to declare the variable inside the block it’s supposed to be
elseif startsWith(txtLine, "char") and not startsWith(txtLine, "chars") then
dim as TBMFontGlyph tempGlyph
' the rest of the implementation
end if