1
1
Fork 0
mirror of https://github.com/boxgaming/qbjs.git synced 2024-05-12 08:00:12 +00:00
42 QBasic Language Support
boxgaming edited this page 2023-09-27 15:39:34 -05:00

QBJS supports a large subset of the QBasic and QB64 syntax as well as native support for the GX API.
The following standard keywords are currently implemented: Supported Keywords.

QBasic Compatibility

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.

Differences from QBasic

  • All variables must be declared
  • 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.
  • Bitwise operators are unsupported at present (with the exception of XOR)
  • Limited support of metacommands

Functionality New to QBJS

  • 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)
  • Inline javascript
  • Support for HTML DOM manipulation, event handling and standard web dialogs
  • Support for browser local and session storage
  • Support for Import of external modules. See Import / Export.

Known Issues

Outstanding issues and requested features are tracked here:
https://github.com/boxgaming/qbjs/issues

Variable Declaration

All variables must be declared with a DIM or REDIM statement. Errors may occur if a variable is referenced before it is declared.
Static variables are not currently supported but this is planned.

Data Types

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

Flexible typing

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 a

Pass by Value

With 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 Function

In 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 Sub

If 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 Sub

We 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

Operators

  • All mathematical operators are supported with the exception of the integer division operator ( \ ). At present integer division operators are just evaluated as a normal division ( / ) operation.
  • All logical operations are supported with the exception of XOR
  • Bitwise operations are not supported at present with the exception of XOR

Associative Arrays

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")

Metacommands

QBasic 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 If

Any 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 Sub

Full support of the $If, $ElseIf, $Else and $Include metacommands is planned for future inclusion.