BINSIC – Binsic Is Not Sinclair Instruction Code – is a BASIC-like language implemented as (similar to) a domain specific language in Groovy.
Update (April 2020): I have worked on getting BINSIC back in an easily usable state and a jar file is now available at http://n4decameron.org/~adrian/binsic.jar .
BINSIC is modelled on the ROM BASICs built in to the Sinclair ZX80 and ZX81 (a manual for that can be found here) – though there are a few notable differences:
- BINSIC supports the String functions
MID$(both as a string extraction and a string insertion function) and
RIGHT$which should be used instead of the string splicing methods supported in the ZX80/ZX81 (in fact string splicing is supported if used as Groovy ranges but not in a true BASIC fashion);
GOTOinside a loop to a line inside a loop will cause a crash (
GOTOcan be used to exit a loop, however), while
GOSUBinside a loop works (though loop variables drop out of scope – Groovy’s scoping rules will apply in BINSIC);
- While Sinclair BASIC only supported
IF .. THEN .., BINSIC will support
IF .. THEN .. ELSE ..;
UNPLOTcommands are supported through a separate graphics pane;
- Capitalised text
LIKE THIS OR THISis best avoided in string literals (and be careful with variable names) as it may get mangled by the various regular expressions that are used to parse the input BASIC script
An example: Conway’s Game of Life
With the above caveats, and with the warning that there may be others and bugs I have not yet picked up, BINSIC is a fully featured and, I am sure, Turing complete language. The example below, of a version of Conway’s “Game of Life”, hopefully illustrates the point:
10 REM Game of Life 20 PRINT "Conway's Game of Life" 30 PRINT "Copyright Adrian McMenamin, 2012" 35 PRINT "firstname.lastname@example.org" 40 PRINT "Licensed under the GPL version 3" 50 DIM A(48, 70) 60 DIM B$(24) 70 PRINT "Please enter your pattern" 75 PRINT " - up to 24 lines of 70 characters" 80 FOR I = 1 TO 24 90 INPUT B$(I) 95 LET T = 0 97 IF B$(I) = "DONE" THEN LET T = 1 98 IF T = 1 THEN LET B$(I) = "" 100 IF T = 1 THEN GOTO 150 110 PRINT B$(I) 120 NEXT I 150 REM Parse Input 160 LET P = 0 170 LET G = 0 175 LET RR = 0 177 LET NN = 0 180 FOR Y = 1 TO 24 185 LET RR = Y 190 LET Z = LEN B$(Y) 210 IF Z = 0 THEN NEXT Y 220 FOR Q = 1 TO Z 225 LET NN = Q 228 LET A(Y, Q) = 0 230 IF MID$(B$(Y), Q, 1) = " " THEN LET A(Y + 24, Q) = 0 235 IF MID$(B$(Y), Q, 1) &amp;amp;amp;lt;&amp;amp;amp;gt; " " THEN GOSUB 10000 240 NEXT Q 250 FOR Q = Z + 1 TO 70 260 LET A(Y, Q) = 0 265 LET A(Y + 24, Q) = 0 270 NEXT Q 280 NEXT Y 300 REM Display Map 310 PRINT 320 PRINT 330 PRINT 340 PRINT "Generation ", G, " Population is ", P 350 FOR M = 1 TO 24 360 FOR N = 1 TO 70 370 IF A(M + 24, N) = 1 THEN PLOT(N, M) 375 IF A(M + 24, N) &amp;amp;amp;lt;&amp;amp;amp;gt; 1 THEN UNPLOT(N, M) 380 NEXT N 390 NEXT M 400 REM Map next generation 410 FOR M = 1 TO 24 420 FOR N = 1 TO 70 430 LET A(M, N) = 0 440 IF M + 1 &amp;amp;amp;lt; 25 AND A(M + 25, N) = 1 THEN LET A(M, N) = A(M, N) + 1 450 IF M - 1 &amp;amp;amp;gt; 0 AND A(M + 23, N) = 1 THEN LET A(M, N) = A(M, N) + 1 460 IF N + 1 &amp;amp;amp;lt; 71 AND A(M + 24, N + 1) = 1 THEN LET A(M, N) = A(M, N) + 1 470 IF N - 1 &amp;amp;amp;gt; 0 AND A(M + 24, N - 1) = 1 THEN LET A(M, N) = A(M, N) + 1 480 IF M - 1 &amp;amp;amp;gt; 0 AND N - 1 &amp;amp;amp;gt; 0 AND A(M + 23, N - 1) = 1 THEN LET A(M, N) = A(M, N) + 1 490 IF M - 1 &amp;amp;amp;gt; 0 AND N + 1 &amp;amp;amp;lt; 71 AND A(M + 23, N + 1) = 1 THEN LET A(M, N) = A(M, N) + 1 500 IF M + 1 &amp;amp;amp;lt; 25 AND N - 1 &amp;amp;amp;gt; 0 AND A(M + 25, N - 1) = 1 THEN LET A(M, N) = A(M, N) + 1 510 IF M + 1 &amp;amp;amp;lt; 25 AND N + 1 &amp;amp;amp;lt; 71 AND A(M + 25, N + 1) = 1 THEN LET A(M, N) = A(M, N) + 1 520 NEXT N 530 NEXT M 540 LET P = 0 600 FOR M = 1 TO 24 610 FOR N = 1 TO 70 611 LET ZZ = 0 612 LET SC = A(M, N) 613 IF A(M + 24, N) = 1 THEN LET ZZ = 1 614 LET RES = 0 615 IF ZZ = 0 AND SC = 3 THEN LET RES = 1 616 IF ZZ = 1 AND (SC = 2 OR SC = 3) THEN LET RES = 1 617 LET A(M + 24, N) = RES 618 LET P = P + RES 650 NEXT N 660 NEXT M 700 PAUSE 50000 800 LET G = G + 1 900 GOTO 310 10000 LET A(RR, NN) = 1 10010 LET A(RR + 24, NN) = 1 10020 LET P = P + 1 10030 RETURN
This code works well, as the screenshot (of an older version that did not use the
PLOT command) hopefully proves:
BINSIC can be downloaded from here: http://n4decameron.org/~adrian/binsic.jar and the Life code from http://220.127.116.11/life.bas. Although this was written in Groovy, Java users (not Android) should be able to run it e.g. via
java -jar binsic.jar life.bas without any difficulty.
DSLs and all that
A domain-specific language (or DSL for short) is a computer language designed for use in a particular environment (as opposed for general use), typically by technically skilled non-programmers. In that sense BASIC (and BINSIC) is not really a DSL at all, in that it is a general purpose language, but Groovy, which builds on Java, is a good choice for implementing DSLs and is often used for this – many of the same methods that would be used to create a true DSL were used to produce BINSIC (though in other ways BINSIC is closer to an interpreter than a DSL).
The two key parts of BINSIC are a preprocessor, which parses the inputted BASIC, and either converts it into native Groovy or places a call to a method in the script which is then handled by the second major part, an interpreter class (which extends Groovy’s scripting capabilities). This means it would be possible to mix Groovy and BASIC in the input script (though that is not recommended).
My guide to doing all of this was Groovy for Domain-Specific Languages – which I would recommend as a general guide, though with the warning that the Kindle edition is poorly formatted and some of the code examples show signs of sloppy editing.
My initial impetus came from the thoughts outlined in the blog here – A reason why kids don’t do programming any more? – and I thought instead of just moaning about it, why didn’t I get on and do something about it? As it happens the
PLOT command was quite poorly supported at the start and, in any case, building BINSIC has made me wonder if there is really much point in teaching kids how to program in such an awful language (RaspberryPi promoters please note). On the other hand some parts of BASIC are enormously expressive – the
INKEY$ primitives really struck me in this way. Indeed, now
UNPLOT more or less work as expected (albeit on a stand-lone graphics pane) I have used BINSIC to write some “quick and dirty” plotting programs. But the graphics are crude and very chunky.
Anyway, have fun and let me know about bugs and comments.
It’s important to add this is all free software (GPL version 3) and the source code is available at https://github.com/mcmenaminadrian.
- Life: rewritten (cartesianproduct.wordpress.com)
- Domain specific languages in django apps – Matthieu Amiguet (reinout.vanrees.org)
- Why I prefer Scheme to Haskell (slidetocode.com)
- Advanced domain-specific languages (scalamacros.org)
- The true role of domain specific languages (frankmccabe.wordpress.com)
- Building a simple query DSL with peewee ORM (charlesleifer.com)
- The argument for domain-specific languages (usdlc.wordpress.com)
- What is a DSL? (usdlc.wordpress.com)
- Too deep (ormomg.blogspot.com)