Cheesecake BASIC was an exercise in compiler-writing. The goal of the exercise was to produce a self-compiling compiler supporting a syntax compatible with QuickBasic.

The end-result was a bytecode-based compiler/interpreter that understands a language compatible with QuickBasic. The compiler itself can be compiled with any of the following compilers:
  • Cheesecake BASIC (the compiler complies itself).
  • PowerBasic console compiler 2.0 or above
  • QuickBasic 4.x
  • PowerBasic compiler for DOS (compiles, but refuses to run :( )

The stages of the project proceeded as follows:

Version 0
Version 0 of Cheesecake BASIC is a simple interpreter. Each line is parsed every single time that that line is met and it is executed as it is being parsed.  The Cheesecake BASIC V0 language is very similar to ancient BASIC interpreters such as BASICA or GW-BASIC. The interpreter is written in QuickBasic. The interpreter has a lot of limitations in the number of BASIC lines that it can handle, the number of nesting levels in FOR loops, the number of parameters to functions etc.

Even though the interpreter is pretty small, it stretched the capabilities of QuickBasic, proving that further developing the interpreter in the QuickBasic environment would not be feasible.  The code was ported to the PowerBasic Console  Compiler, resulting in 2 sets of sources (one for each compiler).

The decision was made to develop the BPP BASIC pre-processor in order to be able to use different compilers with a single set of sources in future versions of Cheesecake BASIC. This way further development could be done in PowerBasic, while still be able to compile the sources in QuickBASIC.
Documentation

Downloads
The following downloads include the Cheesecake BASIC interpreter executable, the source code, and documentation.
Version 1
Version 1 of Cheesecake BASIC is just a re-write of the version 0 sources so that they can be processed through the BPP BASIC pre-processor. The result is source that can be compiled with either QuickBasic, PowerBasic for DOS, or PowerBasic Console Compiler.

Otherwise, the interpreter is exactly the same as version 0. The resulting code has a lot of bugs, but I decided not to bother fixing them, and continue with the next version of the interpreter.

This was basically a proof-of-concept of the BPP BASIC pre-processor and the feasibility of having a single set of sources for all the different BASIC compilers at hand.
Documentation

Downloads
Executables:
Sources:
Version 2
Version 2 of Cheesecake BASIC supports a language very similar to Version 0, although it adds a few features that make it more compatible with GW-BASIC. It also eliminates all the known bugs in version 0, and removes the multiple limitations of version 0.

Internally, version 2 has the following advantages over version 0:
  • A library was implemented supporting different data structures (stacks, queues etc), as well as different memory managers.
  • The parser is based on the version 0 parser, but instead of executing the code as it is being parsed, each line is converted to a tokenized form. Each line is parsed only once. Internally, the entire program is stored in tokenized form.  The execution engine executes the tokenized code, resulting in an interpreter orders of magnitude faster than the previous one.
Documentation

Downloads
The following downloads contain the Cheesecake BASIC executables for a specific platform, and the Cheesecake BASIC sources already pre-processed for a particular compiler. The following download contains the original set of sources from executables for all platforms can be produced. the BPP pre-processor must be used to generate compiler-specific sources. If you want programs that can run under Cheesecake BASIC v2, here are some.
Version 3
Version 3 of Cheesecake BASIC uses the same one-line-at-a-time parser used in versions 0 and 2. Instead of executing the line being parsed (as in version 0) or tokenizing it (as in version 2), this version generates bytecode for a virtual machine, called the Cheesecake Virtual Machine (CVM).

Once the entire program has been converted into bytecode, the bytecode is linked and then it is either 1) executed by the CVM or 2) an executable file is generated. An executable file consists of a CVM, and the bytecode is appended to the file. When the resulting .EXE file is executed, the CVM loads the bytecode from the file and then executes it.

The capability of producing these executable files is why I refer to version 3 as a compiler (rather than an interpreter).

Initially version 3 was supposed to simply implement bytecode generation, and a subsequent version would implement an entirely new parser that would implement a QuickBasic-compatible language.

However, I decided to remove the line-number requirement of previous versions of the interpreter. Once this was done, I added the support for SUB/END SUB and FUNCTION/END FUNCTION. Once that was done, I saw that it would be easy to add support for multi-line IF-THEN-ELSE-END IF. After that, supporting SELECT CASE statements was not too hard...

The result is that after a little while, version 3 ended up supporting a language compatible with QuickBasic, using the same line-oriented parser of version 0 (rather than the more decent parser originally envisioned for version 4).

After a few hacks, version 3 ended up being able of self-compilation. With this, version 3 achieved the goal of the Cheesecake BASIC experiment: A self-compiling compiler supporting a language compatible with QuickBasic, and which itself can be compiled with QuickBasic.
Documentation

Downloads
The following are the executables for the different platforms. No source code is provided. The following is the Cheesecake BASIC source code. You must use the BPP pre-processor to generate the source code that can be compiled by either QuickBASIC, PowerBasic or Cheesecake BASIC V3.
Post-Mortem
The obvious follow-ups for Cheesecake BASIC V3 include:

Replace the incredibly ugly line-oriented parser for a block-oriented parser.

The current parser is a hack, but it works. If I was going to replace the parser, I'd rather write a new compiler, and I would probably use a different implementation language.

Implement a more modern language, including object-orientation.

Yeah, that would be nice.

Port the CVM to other operating systems.

The current Cheesecake Virtual Machine is written in BASIC, which means that executables can only be generated for operating systems for which there is a PowerBasic compiler. Writing a new CVM in C or C++ would allow me to port Cheesecake BASIC to any operating system.

I wrote a Cheesecake Virtual Machine in C. It implements all the bytecodes. However, I have not ported all the "system calls", i.e. the BASIC environment support. I ported some of the basic ones, and was able to generate simple executables using the portable C-based CVM.  However due to lack of interest, I did not proceed any further.