GithubHelp home page GithubHelp logo

qbee's Introduction

qbee ๐Ÿ

qbee is a QBASIC compiler written in Python. The compilation target is a virtual machine called QVM. Most of the core language is complete, but graphics routines are mostly missing. qbee can compile and run the classic nibbles example:

$ python -m qbee.main -o nibbles.mod -O3 -v nibbles.bas
$ python -m qvm.run nibbles.mod

Debugger

qbee has a source-level debugger capable of tracing code, setting breakpoints and evaluating variables and expressions at run-time. In order to be able to use it, you need to compile the source using the -g compiler option.

$ python -m qbee.main -o nibbles.mod -O3 -v -g nibbles.bas

Then you can run the compiled module in the debugger:

python -m qvm.dbg nibbles.mod

You can step through the program using next and step commands, set breakpoints using break, and evaluate expressions using print. Run help to get a full list of all available debugger commands.

QVM

You can find a description of the QVM virtual machine, and its instruction set at the project wiki on github.

Here's a short example so that you can see what the QVM assembly looks like:

$ cat >foo.bas <<EOF
> defint a-z
> input i
> if i < 10 then
>    print "too small"
> else
>    print "too big"
> end if
> EOF

$ python -m qbee.main -O3 --asm -o- foo.bas
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.literals
    0 string ""
    1 string "too small"
    2 string "too big"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.routines

_main:
    integer i

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.code

    call        _sub__main
    halt
_sub__main:
    frame       0, 1
    push0%
    push$       ""
    pushm1%
    push1%
    push1%
    io          terminal, input
    storel      i
    readl%      i
    push%       10
    cmp
    lt
    jz          _else_2
    push0%
    push$       "too small"
    push2%
    io          terminal, print
    jmp         _endif_1
_else_2:
    push0%
    push$       "too big"
    push2%
    io          terminal, print
_endif_1:
    ret

Tests

qbee has a fairly extensive test suite. You can run the tests using pytest: python -m pytest

Compatibility

Python 3.9 or higher is required. This is needed because:

  • Python 3.9 allows having classmethod and property decorators on the same method.
  • It allows using indexed built-in types for type hinting (like list[int], instead of List[int])

qbee's People

Contributors

elektito avatar

Stargazers

 avatar

Watchers

 avatar

qbee's Issues

Add fixed-string support (STRING*n)

QB supports fixed strings declared like:

DIM x AS STRING * 10

x will not hold anything larger than 10 characters, and if a larger string is assigned to it, it is truncated.

Fixed-strings can also be used in TYPE blocks.

Eliminate stores without read

In a snippet like this:

    push1!
    storel!     x!
    push!       2.0
    storel!     x!

The first two instructions can be eliminated, because the second pair overwrites it. This is more difficult than current optimizations that only work on consecutive instructions. This could also span many instructions.

DECLARE statements are not matched against SUB/FUNCTION blocks

We can safely ignore DECLARE statements and use the actual SUB/FUNCTION definitions, as we do right now. However, we should make sure the DECLARE statements match with the SUB/FUNCTION blocks.

Notice that:

  • DECLAREs might drop the arguments, which means argument checking (both count and type) is disabled
  • DECLAREs can have "as any" clauses which disable type checking for particular parameters

Move get_variable_type to the Routine class

Right now get_variable_type is in Compiler and receives a Routine object. It might make more sense to move it to the Routine class. For global variables we can refer to the compiler object. DEF* statements are effective only in the current routine, so this causes cleaner code for them as well.

Periods in identifier names

Without any conflicting declarations, variable names can contain periods. For example, the following is valid:

f.x = 100
PRINT f.x

This also works with SUB/FUNCTON names, but not with DIM statements however. The following is invalid and will result in an error saying "Identifier cannot include period":

DIM f.x AS INTEGER

Interestingly, the following code will result in the same error ("identifier cannot include period") on the assignment line, even though the DIM statement determining the variable type is after that:

TYPE foo
    x AS INTEGER
END TYPE

f.x = 100  ' <-- ERROR: Identifier cannot include period
DIM x AS foo

This does not mean that in general DIM statements work on variable names used before them. The following will cause a "duplicate definition" error on the DIM statement:

x = 10
DIM x AS INTEGER  ' <-- ERROR: Duplicate definition

Caveat for converting float to int

When converting a float value to an int value, in case of overflow, we get max negative int. For example in:

a% = 40000.1

a% would get -32768. The following would cause an overflow error however:

a% = 40000

Add support for full LOCATE command

Currently we only support a LOCATE command accepting a row and column. The original command has more optional arguments. Implement the full command.

RETURN without GOSUB

Encountering a RETURN statement without a GOSUB active would cause the "RETURN without GOSUB" error in QB. Currently we do not handle this case and the return statement would just throw away an active call frame (in case of a simple RETURN statement), or read a value from stack and attempt to jump to it (in case of "RETURN " statement). Both would be incorrect, and would likely cause a crash.

Remove some simple cases of unreachable code

In the absence of a complete graph analysis, we can still remove some simple cases like:

  • Anything from a "ret" instruction until next "_label"
  • Anything from a "jmp" instruction until next "_label"

One example of the first case being created by the compiler, is at the end of an if block, right before an "else" or an "elseif", if there is an "EXIT SUB/FUNCTION" statement. The compiler generates a JMP instruction afterwards to the end of the IF block, which is unreachable.

Implicitly defined arrays

If an array is used without a DIM statement, it's implicitly defined as an array of size 10. For example:

x(3) = 30
PRINT x(3)   ' <-- prints 30
PRINT x(5)   ' <-- prints 0
PRINT x(15)  ' <-- ERROR: Subscript out of range

Notice that without subscript, the variable is considered a different variable:

x(3) = 10
x = 4
PRINT x(3); x  ' <-- prints " 10 4"

Add support for full LOCATE command

Currently we only support a LOCATE command accepting a row and column. The original command has more optional arguments. Implement the full command.

Caveats in variable naming and conflicts

The following cases should be added to vars.test file, and pass.

Duplicate definition:
x% = 10
dim x as

Duplicate definition:
dim x as single
x% = 2

Duplicate definition:
dim x a s single
print x%

No error; x and x! are the same variable:
dim x as sinle
x! = 2

No error; x and x% are separate variables:
dim x
x% = 2

No error; x and x! are the same variable:
dim x
x! = 5

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.