-
Notifications
You must be signed in to change notification settings - Fork 12
QBasic Language Support v0.10.0
QBJS supports a large subset of the QBasic and QB64|PE syntax as well as native support for the GX API.
The following standard keywords are currently implemented: Supported Keywords.
Much effort has been made to allow QBJS to be as compatible as possible with applications built with classic QBasic and QB64. At the same time, another aspiration of this project is to allow access to powerful features of the browser's javascript engine. In an effort to balance these goals the following differences exist between QBJS and QBasic/QB64.
- Subs and function parameters are pass by value instead of pass by reference
- Goto and GoSub are not supported. Labels are supported only in the context of Data/Read/Restore.
- Limited support of metacommands
- Functions can return arrays and custom types
- Functions do not require type suffix to return non-single values
- QBJS takes advantage of flexible typing
- Support for associative arrays (i.e. hashtable, dictionary)
- Support for inline Javascript
- Support for external modules. See Import / Export.
- Standard extension modules to support:
- HTML DOM manipulation, event handling and standard web dialogs
- Browser local and session storage
- IDE console logging
- Extended 2D graphics functionality
- Extended file I/O
- Advanced string utilities
- Support for method pointers
Outstanding issues and requested features are tracked here:
https://github.com/boxgaming/qbjs/issues
All data types from QBasic and QB64 (including custom types) are supported with the exception of the following:
- Fixed length strings (String*n)
- _BIT type with specific length (_BIT*n)
- _MEM data type
QBJS takes advantage of the fact that the underlying Javascript runtime is not strongly typed. This allows for a more flexible variable assignment than would have been allowed in traditional QBasic. For example, the following is valid in QBJS, but would cause an error in traditional QBasic:
Dim a As Integer
a = 3
Print a
a = "test"
Print aWith traditional QBasic, values passed as arguments to functions and subs are passed by reference by default. Any changes made to those values in the method will be reflected in the variable which was passed after the method execution has completed. In QBJS, however, all values are passed by value. Consider the following example:
Dim x As Integer
x = 3
Print "result: "; Increment(x)
Print "x: "; x
Function Increment (n As Integer)
n = n + 1
Increment = n
End FunctionIn QBasic/QB64 this would output:
result: 4
x: 4
However, in QBJS the result would be:
result: 4
x: 3
To be more precise, what is passed by value in QBJS is the pointer to the original value. This means that while assigning the variable to a new value will not affect the value of the original variable (as shown above), if the value passed as the method argument is a complex variable (i.e. a custom type or array) and an element of that variable is modified, the change will be also reflected in the original variable. For example:
Type Position
x As Integer
y As Integer
End Type
Dim testPos As Position
testPos.x = 35
testPos.y = 45
UpdatePosition testPos
Print "x: "; testPos.x
Print "y: "; testPos.y
Sub UpdatePosition (p As Position)
p.x = p.x * 2
p.y = p.y + 10
End SubIf executed in QBasic/QB64 or QBJS, the result will be the same:
x: 70
y: 55
However, if we alter this example slightly to assign a new value to the p variable like so:
Type Position
x As Integer
y As Integer
End Type
Dim testPos As Position
testPos.x = 35
testPos.y = 45
UpdatePosition testPos
Print "x: "; testPos.x
Print "y: "; testPos.y
Sub UpdatePosition (p As Position)
Dim p1 As Position
p1.x = p.x * 2
p1.y = p.y + 10
p = p1
End SubWe will see the same result as above when executed with QBasic/QB64. However, in QBJS, because we reassigned what p was pointing to in the UpdatePosition function, this results in the original values being printed in the program output:
x: 35
y: 45
In addition to support for standard QBasic arrays, QBJS also provides the ability to create associative arrays, also called hashtable, map, or dictionary.
Dim stnames() As String
stnames("CA") = "California"
stnames("NY") = "New York"
stnames("KY") = "Kentucky"
Print "Name: "; stnames("KY")QBJS adds the ability to define Functions and Subs as variable types. Additionally, pointers to existing subs or functions can be referenced with the '@' operator. One of the primary use cases for this feature is to support passing "callback" methods for event-based programming. In the example below we can define a method to handle button click events and then pass a reference to that method to register it with the Dom.Event method so that it can be called by the framework each time the button is clicked.
Import Dom From "lib/web/dom.bas"
Dim btn As Object
Dom.Create "br"
btn = Dom.Create("button", , "Push Me")
Dom.Event btn, "click", @OnButtonClick
Sub OnButtonClick (event As Object)
Print "You pushed it!"
End SubFunction and Sub variable types can be used for creating custom callback functions as well as enabling dynamic method assignment:
Randomize Timer
' Dynamically assign a function reference and call it
Dim fn As Function
fn = @Emphasize
Print fn("This is working")
Print "---------------------------------------"
' Pass a callback function to another method
Delegate "Is this a question", @Question
Delegate "Yes it is", @Emphasize
Print "---------------------------------------"
' Initialize a list of function references as objects
Dim As Object funcRefs(15)
For i = 1 To UBound(funcRefs)
If Rnd*2 > 1 Then
funcRefs(i) = @Emphasize
Else
funcRefs(i) = @Question
End If
Next i
For i = 1 To UBound(funcRefs)
fn = funcRefs(i)
Print i; fn("This is a function")
Next i
Function Emphasize (s As String)
Emphasize = s + "!!"
End Function
Function Question (s As String)
Question = s + "?"
End Function
Sub Delegate (s As String, fnCallback As Function)
Print fnCallback(s)
End SubQBasic and QB64 support a number of metacommands prefixed with '$' (e.g. $Include, $Dynamic). All metacommands are currently ignored by QBJS with two exceptions. Any content included between the $If Javascript Then ... $End If will be evaluated as native javascript.
Dim n as Integer
Input "Enter a number: ", n
If n > 8 Then
$If Javascript Then
alert("That number is too large");
$End If
End IfAny content included within a "$If WEB Then" block will be evaluated by QBJS but ignored by QB64. This allows for web-only code blocks to be defined.
Sub FCirc (CX As Long, CY As Long, R As Long, C As _Unsigned Long)
$If WEB Then
G2D.FillCircle CX, CY, R, C
$Else
Dim Radius As Long, RadiusError As Long
Dim X As Long, Y As Long
Radius = Abs(R): RadiusError = -Radius: X = Radius: Y = 0
If Radius = 0 Then PSet (CX, CY), C: Exit Sub
Line (CX - X, CY)-(CX + X, CY), C, BF
While X > Y
RadiusError = RadiusError + Y * 2 + 1
If RadiusError >= 0 Then
If X <> Y + 1 Then
Line (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
Line (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
End If
X = X - 1
RadiusError = RadiusError - X * 2
End If
Y = Y + 1
Line (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
Line (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
Wend
$End If
End SubFull support of the $If, $ElseIf, $Else and $Include metacommands is planned for future inclusion.