Releases: ngeor/rusty-basic
v0.8.0
v0.7.0
Releasing version 0.7.0
v0.6.1
Releasing version 0.6.1
v0.6.0
πππ This is a significant milestone for rusty-basic, as it is capable of interpreting the MONEY.BAS
program that was part of QBasic
MONEY.BAS
If you have a copy of MONEY.BAS
lying around you can give it a try. You'll need to convert it to UTF8 first, as rusty-basic can't handle OEM/ASCII charset yet. Everything should work as it did in QBasic, with the exception of printer support which isn't implemented. I've only tested it on Windows, but the crossterm library used for the console should work on multiple environments.
Added
- Support for
SHARED
variables - Implemented
GOSUB
,RETURN
andEND
- Implemented
EXIT SUB
andEXIT FUNCTION
- Implemented
RESUME
- Support for multiple expressions in a single
CASE
statement - Implement
STATIC
subs - Support for multiple variables in a single
DIM
statement - Implemented
DO
loop - Implemented modulo (
MOD
) operator - Support for random mode files
- Implemented
VIEW PRINT
- Implemented
LOCATE
- Implemented
DATA
andREAD
- Stub implementation for
WIDTH
- Implemented built-in functions
STRING$
,LEFT$
,RIGHT$
,LCASE$
,UCASE$
,CVD
,MKD$
,LTRIM$
,RTRIM$
,INKEY$
,ERR
,SPACE$
- Implemented memory manipulation statements
DEF SEG
,PEEK
,POKE
,VARPTR
,VARSEG
- Implemented
CLS
andCOLOR
- Implemented
REDIM
- Implemented
BEEP
(only for Windows)
Bugfixes
- Supporting keywords as string variable names (e.g.
END$
is allowed,END%
is not allowed) - Allowing whitespace after sub name
- Fixed bug with parsing
IF
statements with expressions in parenthesis - Allowing sub/function declarations to be left unimplemented (as long as they're not used, QBasic doesn't seem to mind)
- Fixed bugs in priority of binary expressions
- Support array elements and user defined properties for
LINE INPUT
- Trimming whitespace in
INPUT
- Support more formatting characters in
PRINT USING
, namely\
,!
, and the thousands separator,
. LEN
function was reporting UTF8 byte length, switched to ASCII to ensure 1 byte = 1 char expectation of QBasicSTR$
function should print leading space for non-negative numbers
v0.5.0
The highlight of version 0.5.0 is the support for arrays!
Summary
Added
- Support for arrays
- Implemented print zones and limited support for format strings
CLOSE
without arguments- Octal and hexadecimal literals
- Support for fixed length string variables
Fixed
- Ensuring for loop variable is numeric
- Bugfix: arguments to subprograms could appear in random order, due to usage of HashMap in the implementation
Details
Array support
The most important feature in this release is the support for arrays. It is possible to define (DIM
) arrays of multiple dimensions, read and write array elements, even pass arrays as parameters.
DIM squares(1 TO 10)
FOR I = 1 TO 10
squares(I) = I * I
NEXT
Mind the parenthesis when passing an array as parameter:
DECLARE SUB PrintArray(numbers%())
DIM numbers%(1 TO 5)
PrintArray numbers%()
SUB PrintArray(numbers%())
FOR I = LBOUND(numbers%) TO UBOUND(numbers%)
PRINT numbers%(I)
NEXT
END SUB
Implementing LBOUND
and UBOUND
I found an inconsistency (in my opinion) in QBasic. When passing an array to a user defined function / sub, the empty parenthesis following its name are required. When passing an array to LBOUND
/ UBOUND
functions, the parenthesis must not be used (as shown in the above example). As always, rusty_basic
strives to not diverge from QBasic implementation.
Print zones
The syntax of the PRINT
statement is rather complicated and custom. Arguments to functions or subs are separated by commas. In PRINT
, the comma is more than a separator. It determines that the output of the next argument will happen on the next print zone. Print zones are 14 characters wide.
It is possible to use a semicolon instead of a comma, in which case the argument will be printed immediately after the last value.
Consider these two examples:
PRINT "a", "b"
PRINT "a"; "b"
The first line will print "a", then 13 spaces, then "b".
The second line will print "ab", without any space between them.
It gets more complicated when the arguments are numbers, where an implicit whitespace is added if the number is positive. I don't remember it off the top of my head but I sure wrote lots of unit tests for it :-D
It's even possible to mix and match commas, semicolons, have commas followed by commas without any argument in between, and so on. This required a special parser implementation just for the PRINT
statement, which is too different from the rest (especially because a comma is something significant that needs to be preserved at runtime).
Format strings
Another addition to the PRINT
statement is the PRINT USING
variation. This allows the user to control the output of an expression with a format string e.g.
PRINT USING "###.##"; 123.456
will print 123.46 (rounding to two digits after the decimal point).
The documentation doesn't quite specify what will happen if the number of placeholders in the format string doesn't match the number of passed arguments. Consider the following code:
PRINT USING "A: # B: # C"; 1, 2, 3
Note that there are 2 placeholders but 3 arguments. In this case it seems printing will start over but stop as soon as it run out of arguments, so it prints "A: 1 B: 2 CA: 3 B: ".
This feature is partially implemented, covering only the #
placeholder. The documentation mentions more placeholders, but it's quite unclear to me how they're supposed to behave.
CLOSE
without arguments
Implemented support for calling CLOSE
without arguments, which closes all open files.
Octal and hexadecimal literals
It's now possible to use octal PRINT &O10
and hexadecimal PRINT &HFF
literals.
Support for fixed length variables
It's now possible to define a variable of a fixed length string. This was previously implemented only for elements of user defined types.
DIM PostCode AS STRING * 10
It is not possible in QBasic to have a function / sub parameter in this way. But it is possible to pass a fixed length string as a parameter (and it's by reference) to a sub that expects a string:
DIM TwoLetters AS STRING * 2
TwoLetters = "AB"
PRINT TwoLetters
Modify TwoLetters
PRINT TwoLetters
SUB Modify(X$)
PRINT X$
X$ = "XYZ"
PRINT X$
END SUB
The above program prints:
AB
AB
XYZ
XY
So the fixed length string variable is passed by reference (or sort of) but while it's inside the sub it can grow to more than its declared length. Immediately after exiting the sub, it's trimmed down to 2 characters. This surprising discovery took some time to implement and affected the way parameter passing is implemented in general in rusty_basic
.
v0.4.0
New features in v0.4.0:
- Support for user defined types
- Support for variable names containing periods
- Support
DIM var-name AS var-type
syntax for built-in types (integer, long, single, double, string)
Fixes:
- Limiting identifiers to 40 characters
- Ensuring file numbers are between 1 and 255
On the technical side, the parser was practically rewritten using parsing combinators.
v0.3.0
This is the first release after moving rusty-basic to its own repository.
Milestone: rusty-basic is able to run the TODO.BAS program originally mentioned in this blog post (modified for QBasic).