I'm trying to teach QuickBasic to do codegolf (and myself in the process), so I'm creating the language Quick Basic Interpreter for Codegolf, or QBIC for short.
0 byte factoid
Most of QBIC's strength lies in just making a shorthand for QBasic's expansive syntax, but I've also incorporated several features of my own. When generating the trans-compiled BAS file, a standard header is imported that implements some of these features. For instance, the numbers 1 through 10 get initialised as variables q
through z
. This occasionally saves me some bytes when initialising a FOR loop, and shaves a byte off of using 10
.
1 byte
}
The above statement will do ... exactly nothing. When found in the QBIC code, it wil automatically add END IF's, NEXTs and LOOPs for every opened IF/FOR/DO construct it finds (in the correct order, naturally). Note that ]
does the same for one language construct, where }
closes all opened constructs.
However, since we've not yet opened any of those constructs, this does nothing and results in an 'empty' Qbasic BAS file. Also, at the end of compiling QBIC, the compiler will run this statement automatically.
2 bytes
?z
This will write 10 on the screen. In QBIC, the letters of the lower-case alphabet are references to numerical variables. This is retained from QBasic, where PRINT a
would give me a 0
on the screen (even without ever defining a
). In addition, QBIC initialises q-z to be 1 through 10 by default. The ?
is obviously shorthand for the PRINT
statement and writes to screen.
3 bytes
_Xb
_X
can be used to terminate a program, and when used with a suffix, it will print something on exit. Suffixes can be A-Z (which refers to string variables A$ - Z$) or lowercase a-z (for the numerical vars of this name). The example snippet ends the program, and prints variable b
to screen before it turns off the lights.
4 bytes
?_r|
_r
is a random number generator. By itself, it will generate a random number between 0 and 10 and, in this case, print it. We can also add one or two parameters for a different lower and upper bound; If only one is specified, that's our new upper bound.
_r20|
will generate a number between 0 and 20.
_r5,18|
will generate a number between 5 and 18.
The statement _R
is equivalent to _r
with one exception: _R
assigns to a variable, _r
does not:
?_r| PRINT getNextRandomNumber(0, 10)
_R|?a a = getNextRandomNumber(0, 10) : PRINT a
5 bytes
:_Ra|
The above snippet introduces the :
, which reads the first unread command line parameter and assigns it to the first available numeric var. The resulting QBasic code for this snippet is:
a = assignCMDToNum!
b = getRandomNumber(0, a)
There's also ;
, which does the same for string variables.
6 bytes
?!z$+A
Here, we see a cast in action. QBasic is kinda picky when it comes to data types. Adding a string to an integer will result in errors. To work around this, I've added a casting function. The above snippet will print 10
(z
is auto-initialised to 10
) and append whatever is in the variable A$
. For instance, a full program could be
#test`?!z$+A
and the result then is 10test
.
Casting is done using the !
symbol. It then casts everything between the !
and the delimiter. Usually |
is the delimiter for variable-length arguments, but CAST uses either a $
to perform the cast from number to string, or another !
to cast a string to number.
7 bytes
Z=_f_tA
Lot going on here. Let's work on the assumption that A$
already holds a value, say " Hello " (significant spaces). This snippet then sets Z$
to the reversed (_f
), trimmed (_t
) version of A$
, olleH
and prints it to the screen.
The behaviour between _f
and _F
is different: _F
declares a variable and assigns its output to it, _f
only gives the output and assigning is done by other parts of the script. The same goes for _r
in my 5-byte snippet. This is called ULX, or Upper-Lowercase Extension. Whether or not output gets auto-assigned is one of the behavioural changes ULX can bring, but there are other examples. _D
, for intance, yields the system date, _d
yelds system time. Both don't auto-assign.
_t
removes the whitespace on both sides of its argument. _u
does a left-trim, _v
does right-trim. All three support ULX, and for all three this switches between auto-assign (uppercase) or output only (lowercase).
Both _f
and _t
should be delimited with |
to signify the end of the parameter list. However, since EOF is reached, the correct number of |
's get added to close all opened parameter lists. If this statement would have been followed by the closing commands for language constructs (]
or }
, see byte 1) those same |
's would get added.
QBIC allows for the result of one function to be the input for the next: _f
takes as input the result of _t
Finally, if we would run this program, it would actually print olleH
, despite we never use a ?
or _X*
statement. This is because QBIC implicitly prints the contents of Z$ on exit.
Summarizing, the above 7-byte snippet would roughly compile into this QBasic block:
FUNCTION revstring$(in$)
... function definition imported through QBIC.H
END FUNCTION
... we set A$ to be " Hello "
Z$ = revstring$(LTRIM$(RTRIM$(A$)))
PRINT Z$
8 bytes
Let's look at literals. There are 2 types in QBIC: String literals (Verbal and Silent) and Code Literals. They are started with @
, #
and '
respectively, are terminated with ```. Using a ┘
respresents a line-break.
?@check` --> Verbal string literal: Defines A$ in the header, assign the value "check"
to it, and insert A$ in the body of the resulting QBasic at this moment.
#10`?A+A --> Silent literal: Defines the literal A$ as "10", but doesn't insert `A$`
in the QBasic output at this time. This sample then prints "1010"; `+`in
this context is string concatenation.
'SQR(9)` --> Code literal: from the `'`(which would be seen as a comment by QBasic) to
the ``` is not stored as a variable, but passed unaltered to the resulting
QBasic. This particular statement calculates the root of 9.