Express BASIC is a minimal BASIC dialect that is suitable for performing complex calculations. The language is inspired by Dartmouth, Palo Alto, Altair, GW BASIC, and the many dialects of Tiny BASIC.
It is written in mostly C and is open source. I have been working nonstop on this and am finally at a point I am comfortable sharing it. The syntax is very close to Net Basic. Most programs written in Express BASIC will run in QBasic or GWBasic with little modification.
Programs may be typed directly, loaded with LOAD "filename.bas", saved with SAVE "filename.bas", cleared with NEW, listed with LIST, etc. You may pass source files through the command line, drag and drop, or association.
The interpreter features a classic style line mode editor and will run on all versions of Windows 95 and up. There's just enough commands and functions to be useful. My goal is for this language to be standard and minimal. I don't want to get crazy with the syntax like with Craft Basic and Commando Basic. This will be my "normal" BASIC along side Net Basic.
So, with Net Basic being written in PHP and Express BASIC in C, I think this will be the last language I will make for a long time hopefully.
Get it here: https://lucidapogee.com/index.php?page=expressbasic
Here's a few examples...
10 REM ROCKET.BAS by Brian Tung
20 REM Modified to work with Express Basic
30 LET a = 1.032
40 LET d = 1
50 PRINT "Earth gravity in light-years per year squared: "; a
55 PRINT "Distance in light-years (0-100 million): "; d
60 IF d >= 0 AND d <= 100000000 THEN 80
70 PRINT "Distance must be between 0 and 100 million l-y"
75 END
80 LET d1 = d / 2
90 LET t = SQR(d1 * d1 + (2 * d1 / a))
100 LET x = a * t
110 LET m = 1
120 IF x >= 0 THEN 130
125 LET m = -1
130 LET s = LOG(ABS(x) + 1)
140 LET s1 = s + 1
150 LET x1 = (EXP(s) - EXP(s * -1)) / 2 - ABS(x)
160 LET s1 = x1 / (EXP(s) + EXP(s * -1)) / 2
170 LET s = s - s1
180 IF ABS(s1) > .0000001 THEN 150
190 LET t1 = 1 / a * s * m
200 LET v = a * t / SQR(1 + (a * t) * (a * t))
210 PRINT "Time on Earth: "; 2 * t; " years"
220 PRINT "Time on board: "; 2 * t1; " years"
230 SHELL "pause"
1 REM MODIFIED TO WORK WITH EXPRESS BASIC
10 PRINT "HURKLE"
20 PRINT "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
30 PRINT
110 LET N=5
120 LET G=10
210 PRINT
220 PRINT "A HURKLE IS HIDING ON A ";G;" BY ";G;" GRID. HOMEBASE"
230 PRINT "ON THE GRID IS POINT 0,0 IN THE SOUTHWEST CORNER,"
235 PRINT "AND ANY POINT ON THE GRID IS DESIGNATED BY A"
240 PRINT "PAIR OF WHOLE NUMBERS. THE FIRST NUMBER IS"
245 PRINT "THE HORIZONTAL POSITION AND THE SECOND NUMBER"
246 PRINT "IS THE VERTICAL POSITION. YOU MUST TRY TO"
250 PRINT "GUESS THE HURKLE'S GRIDPOINT. YOU GET ";N;" TRIES."
260 PRINT "AFTER EACH TRY, I WILL TELL YOU THE APPROXIMATE"
270 PRINT "DIRECTION TO GO TO LOOK FOR THE HURKLE."
280 PRINT
285 LET A=INT(G*RND)
286 LET B=INT(G*RND)
290 LET K = 0
310 LET K = K + 1
320 PRINT "GUESS #";K
330 INPUT "X: ", X
335 INPUT "Y: ", Y
340 IF ABS(X-A)+ABS(Y-B)=0 THEN 500
350 REM PRINT INFO
360 GOSUB 610
370 PRINT
380 IF K <= N THEN 310
410 PRINT
420 PRINT "SORRY, THAT'S ";N;" GUESSES."
430 PRINT "THE HURKLE IS AT ";A;",";B
440 PRINT
450 PRINT "LET'S PLAY AGAIN, HURKLE IS HIDING."
460 PRINT
470 GOTO 285
500 REM
510 PRINT
520 PRINT "YOU FOUND HIM IN ";K;" GUESSES!"
540 GOTO 440
610 PRINT "GO ";
620 IF Y=B THEN 670
630 IF Y<B THEN 660
640 PRINT "SOUTH";
650 GOTO 670
660 PRINT "NORTH";
670 IF X=A THEN 720
680 IF X<A THEN 710
690 PRINT "WEST";
700 GOTO 720
710 PRINT "EAST";
720 PRINT
730 RETURN
999 END
1 REM https://rosettacode.org/wiki/Attractive_numbers
10 FOR x = 1 TO 120
20 LET n = x
30 LET c = 0
40 IF n MOD 2 <> 0 THEN 70
50 LET n = INT(n / 2)
60 LET c = c + 1
70 IF n MOD 2 = 0 THEN 40
80 FOR i = 3 TO SQR(n) STEP 2
90 IF n MOD i <> 0 THEN 120
100 LET n = INT(n / i)
110 LET c = c + 1
120 IF n MOD i = 0 THEN 90
130 NEXT i
140 IF n <= 2 THEN 160
150 LET c = c + 1
160 IF NOT(PRIME(c)) THEN 180
170 PRINT x,
180 NEXT x
190 PRINT
200 SHELL "pause"
1 REM https://rosettacode.org/wiki/Golden_ratio/Convergence
5 PRECISION 6
10 LET phi0 = 1
20 LET phi1 = 1 / phi0 + 1
30 LET d = ABS(phi1 - phi0)
40 LET phi0 = phi1
50 LET i = i + 1
60 IF d >= .00001 THEN 20
70 PRINT "result: "; phi1; " after "; i; " iterations"
80 PRINT "the error is approximately "; phi1 - (.5 * (1 + SQR(5)))
90 SHELL "pause"
1 REM https://rosettacode.org/wiki/Nth_root
5 PRECISION 6
10 LET a = INT(RND * 5999) + 2
20 PRINT "nth root of "; a; "..."
30 FOR n = 1 TO 10
40 LET p = .00001
50 LET x = a
60 LET y = a / n
70 IF ABS(x - y) <= p THEN 110
80 LET x = y
90 LET y = ((n - 1) * y + a / y ^ (n - 1)) / n
100 IF ABS(x - y) > p THEN 80
110 PRINT n; " : "; y
120 NEXT n
130 SHELL "pause"
"Mantain" lol this is what happens when I type stuff after midnight and don't spell check. Fixed. Thank you.
Normally I can run Windows programs, on my Linux machine, using Wine. But it looks like EB may be designed for 32 bit. Have no fear... I have a VM.... Moo Ha Ha Ha...
Looking forward to taking EB "out for a spin". Nostalgia. Gotta love it!
Hmmm... Interesting... Not used to keying in capitalized reserved words... But... "When in Rome"... lol
Nice version of Basic. Well done!
I'm happy to bring out some nostalgia 8)
It isn't a case sensitive language. I am writing the examples in caps to make the "true" BASIC like syntax shine.
The thought of running it on Linux is interesting to me. One of my goals with this being written in C is to port it to various platforms.
I am thinking about dos, linux, windows ce, arduino, android.
Hopefully it won't be to difficult to port. It uses a few Windows only library calls that should be easy enough to replace.
Ok... Lowercase works just fine... saves a few key strokes... lol
The SOUND statement does not seem to work... not a concern... Wine is nowhere near 100% compatible with Window commands anyway...
I have also got EB running on Windows XP via a VM. Sound does not work either.... Oh well... Worth a shot...
Going to test EB with some simple Astronomy programs... fingers crossed... fortunately it's all text... lol
J
I'm nervous to see how well it works out for you. I made an effort to make sure all the functions and operators behave as expected, but this project is only a week old. It has had limited testing. Mostly just the included examples.
It will be exciting to see what you find. If anything is wrong, I will do my best to fix the problems.
As I stated earlier, I am not overly concerned about the sound not working, as I am used to a LOT of things going "amiss" with compatibility issues. But, as I work my way through the examples and other minor projects, I will certainly keep you informed of any "random features" that I may encounter... In my case I will test via Wine and the VM....
J
I will be looking towards compiling this for linux. A few changes in the code will have to take place.
For SOUND, ALERT, TIMER, MILLISECS, and a few things I am using windows.h library. This should be easy to omit or replace.
In the windows.h library I am using Beep for sound. I noticed dos.h has a sound function that could be used as well. Not sure what the Linux equivalent is. Stuff like that is just a bonus anyway compared to the core BASIC functions.
I tried adding the allegro.h library, but it conflicts with windows.h. I also tried adding graphics.h and had no luck getting it to work with Dev-C++ that I am using to compile. Finally, I also had no luck with using GDI from windows.h itself. I just need more time to study windows.h. It would be nice to add screen, color, pset, etc.
I also plan to add else/endif eventually. Just don't want to add too much stuff that isn't "standard."
One thing I need to figure out is USING.
This is an intimidating project to tackle. Your feedback is helpful to me. Especially when it comes to the expression evaluation, logic, and structure.
Hit a snag... EB no longer executes via Wine... I am actually surprised that it lasted as long as it did. Looking at the error message I would hazard a guess to say that it looks like 32bit and 64bit are not playing together well...
I will continue testing via my VM...
*sigh*
I'm using the setting to compile for 386 compatibility. Maybe that has something to do with it?
I have been researching various libraries. Hopefully I will get this running with graphics and cross platform compatibility soon-ish.
I tried running an unrelated Win32 programme and it too failed... hmm.. Possible Wine corruption? Purged Wine from the system. Rebooted and installed a fresh version of Wine. EB (and other unrelated executable) seem to be behaving. I must remember this fix... Just in case...
That's good news. Just needed the fresh install.
I have been working on trying to implement graphics and have had no luck with libraries. Ended up borrowing more BCX routines. Amazingly, you can draw to the console directly! Wow!
There's now two versions of Express Basic. Minimal and Extended Editions. The minimal contains no Windows features and should be easy enough to port to Linux while the Extended Edition depends on Windows for graphics commands.
There's now updated links on the download page for the Minimal and Extended Editions.
I'll post the downloads here as well:
Minimal Edition: https://www.lucidapogee.com/download.php?file=expressbasicminimaledition.zip
Extended Edition: https://www.lucidapogee.com/download.php?file=expressbasicextendededition.zip
New features in the extended edition include:
PSET (x, y)
LINE (x1, y1)-(x2, y2)
CIRCLE (x, y), radius
COLOR doscolors
RGB red, green, blue
WAV "sync:filename.wav"
PSET only accepts x and y parameters.
LINE only accepts x1, y1, x2, and y2.
CIRCLE only accepts x, y, and radius
COLOR accepts 0-15
RGB accepts 0-255 for rgb values.
Expressions may be used anywhere in these commands. This includes expressions with ()-(). For example, LINE ((150/2)-(3*2),(14*5)-(6+7))-((123-44)-(5-66),(42*7)-(66+45)) will draw the same line in QBasic and Express BASIC without modification. I went out of my way to require the extra weird syntax with the ()-().
PSET and CIRCLE are similar.
WAV requires sync: or async: in the quotes to the left of the filename. I thought this was a quick and dirty solution.
The Minimal Edition does not have the following commands: ALERT, LOCATE, PSET, LINE, CIRCLE, COLOR, RGB, WAV, SOUND
I changed the TIMER and MILLISECS function to use clock() instead of GetSystemTime(). This removes the windows.h dependency from both editions for randomization seeding, TIMER, and MILLISECS. For timer, I am just using clock()/1000. Millisecs is clock(). The big difference is that now the count is from when the program starts. For the random seeding, I am using time(NULL).
I also fixed a little issue with LOCATE. It was expecting 0, 0 as the top left. Now it accepts 1, 1 like QBasic.
Here's some of the graphics examples included with the Extended Edition:
1 REM https://rosettacode.org/wiki/Draw_a_sphere
5 CLS
10 LET j = 2
20 FOR i = 51 TO 0 STEP j * -1
30 FOR k = PI * -1 TO PI STEP .05
40 PSET (51 + i * sin(k), 52 + 51 * cos(k))
50 PSET (51 + 51 * sin(k), 52 + (i - 1) * cos(k))
60 NEXT k
70 LET j = j + 1
80 NEXT i
90 SHELL "pause"
(https://lucidapogee.com/expressbasic/screenshots/sphere.png)
1 REM https://rosettacode.org/wiki/Barnsley_fern
2 CLS
5 COLOR 10
10 FOR i = 1 TO 10000
20 LET r = RND
30 IF NOT(r > 0 AND r < .01) THEN 60
40 LET x = .0
50 LET y = .16 * y
60 IF NOT(r > .01 AND r < .08) THEN 90
70 LET x = .22 * x - .26 * y
80 LET y = -.23 * x + .22 * y + 1.6
90 IF NOT(r > .075 AND r < .15) THEN 120
100 LET x = .15 * x + .28 * y
110 LET y = -.29 * x + .24 * y + .44
120 LET x = .85 * x + .04 * y
130 LET y = -.04 * x + .85 * y + 1.6
140 LET x1 = (x + 3) * 20
150 LET y1 = 200 - y * 20
160 PSET (x1, y1)
170 NEXT i
180 SHELL "pause"
(https://lucidapogee.com/expressbasic/screenshots/barnsleyfern.png)
1 REM https://rosettacode.org/wiki/Mandelbrot_set
5 CLS
10 LET max = 15
20 LET w = 319
30 LET h = 199
40 LET px = 0
50 LET sy = (py - h / 2) / 150
60 LET sx = (px - w / 2) / 150
70 LET i = 0
80 LET x = 0
90 LET y = 0
100 LET xy = x * x + y * y
110 LET xx = x * x - y * y + sx + .1
120 LET y = 2 * x * y + sy
130 LET x = xx
140 LET i = i + 1
150 IF i < max AND xy < 1 THEN 110
160 RGB 220 + i * x, 220 + i * y, 230 + i * xy
170 PSET (px, py)
180 LET px = px + 1
190 IF px < w THEN 50
200 LET py = py + 1
210 IF py < h THEN 40
220 LOCATE 25, 1
230 SHELL "pause"
(https://lucidapogee.com/expressbasic/screenshots/mandelbrot.png)
1 REM https://rosettacode.org/wiki/Munching_squares
5 CLS
10 LET s = 255
20 FOR y = 0 TO s
30 FOR x = 0 TO s
40 LET r = x XOR y
50 RGB r, r * 2, r * 3
60 PSET (x, y)
70 NEXT x
80 NEXT y
90 LOCATE 25, 1
100 SHELL "pause"
(https://lucidapogee.com/expressbasic/screenshots/munchingsquares.png)
2 CLS
5 COLOR 15
10 FOR f = 0 TO 1000 STEP .1
20 LET x = x + SIN(f * f)
30 LET y = y + COS(f * f)
40 PSET (x + 200, y + 20)
50 NEXT f
60 SHELL "pause"
(https://lucidapogee.com/expressbasic/screenshots/curlicue.png)
There's probably more I am forgetting to mention and even more to do. As always. 8)
There was a problem with CLS where it was clearing the console text, but not the graphics. I applied a simple "hack" to remedy that for now. This only applies to the extended edition where graphics are used.
The IF statement was missing the ability to use an expression (as opposed to a literal value) as the line number, so I fixed that to be consistent with GOTO and GOSUB.
I added the CHR$() function. This is the only string function and for now, it's solely intended to be used with PRINT. This way you may display double quotes and other special characters.
For example PRINT CHR$(34); "Hello, world!"; CHR$(34)
Here is an interesting wrinkle with "sphere". Using 'extended' via Wine... The program coded and ran without error... I was expecting the sphere to be displayed within the console, just as your example, but it actually draw it directly to the upper left corner of the Desktop... As soon as I hit any key, including 'print screen', the sphere would vanish...
"Riddle me that one, Batman".
I will test the other posted examples and get back to you...
By the way... there were no adverse side effects to the system... Just a bit weird... lol
The same thing happened when drawing the "fern". The program executed and was drawn onto the desktop.
It was worth a shot...
This is all crazy and weird to me as well. It started when someone at another forum asked if I will be adding graphics support. Well, this is of course a complicated decision to make in C. There's no standard way, many libraries, and little cross platform solutions.
Especially with the way I am doing things. I wanted to make a console line number mode editor and interpreter like the old days. The complication is that if I want to display graphics, I need to open a window using the console as a parent. How is that even done? From looking at Microsoft documentation, I need to do a "mangled main" and a bunch of stuff that would require a bunch of things to change in a way that would further break portability.
So, using windows.h and gdi in this context seemed too difficult for me. I messed around with Allegro. I finally got it working after realizing it conflicts with windows.h. Problem is it raised my exe size to like 500kb. No way. Not having it.
I tried graphics.h and simply had no luck. I followed steps to setup Dev C++ and the linkers to compile the Borland graphics.h library. When I finally got it to compile the program just crashes and there's no errors left to debug. So I gave up on that for now.
I tried GL. It was too complicated similarly to the windows.h gdi. Gave up on that.
While bouncing around and trying to figure all of this out, I came across some articles discussing unique Windows features and BCX. That's where I got this crazy idea and the functions to draw directly to a console.
https://www.daniweb.com/programming/software-development/code/216430/add-a-little-graphics-to-your-console
https://www.daniweb.com/programming/software-development/code/216431/put-a-bitmap-image-on-your-console-c
http://blog.airesoft.co.uk/2012/10/things-ms-can-do-that-they-dont-tell-you-about-console-graphics/
I decided to create the Minimal and Extended Editions of Express BASIC specifically for Windows using this feature. This way, I would work on making the Minimal Edition more portable. That way I may compile a DOS/9X edition, Linux Edition, and Windows CE edition all separately.
The problem I am having now is that the Extended Edition which is solely intended for Windows.... is sadly only compatible with 32 bit Windows. It doesn't work on 9X and is untested, but probably won't work on anything other than 2000 and XP.
The fact that it partially worked on WINE is interesting. Maybe the results will be similar on Windows 7/10/11. Still waiting for the opportunity to test on newer systems as my newest is XP.
I think what is going on is WINE was unable to allow the program to obtain a handle for the console or something like that. When you use these drawing routines and do not apply a handle, you draw directly to the entire screen over everything. You can create a screensaver like program that takes over the screen like that.
At any rate, I am still keeping this project in the Alpha Version phase, so major changes may still take place. Particularly in the Extended Edition which is very experimental right now. I will likely try finding a better supported graphics implementation. Maybe Visual Studio 2005 is what I need.. Or Borland. Something that can compile from 95 all the way to 11. As opposed to Bloodshed, the noob compiler that I depend on.
Ran the "sphere" via my WinXP Virtual Box...
... and the "fern" ...
Is it running slow? The interpreter is pretty slow, so I imagine that's amplified on a virtual box.
The "fern" does perform 10,000 iterations so, regardless of the Basic, it will be slow... But it wasn't too bad...
The 'sphere' was a little slow... but that could be because it was doing the angles 'on the fly'. Storing and reading the angles using an array could possibly speed up the drawing...
Overall, EB performed quite well...
You mentioned that the interpreter was "pretty slow". Logically, that would imply that, you would have had an actual or mental reference for comparison? Regardless, I think it is pretty cool that there are those who are clever enough to create interpreters! Very cool.. (Note: A not very well hidden compliment... Well done!)
Tried testing the sphere in SpecBAS but it wouldn't run.
Took a while to figure out but it's because you guys (and QB et al) re-evaluate your STEP expressions (at the very least!) every loop in FOR?
In mine the STEP expression is evaluated once when the loop is set up, so of course the example code failed to run.
Work has kept me pretty busy these past few weeks and I am starting a new job tomorrow. Sadly, I haven't had time to be on the computer.
Quote from: johnno56 on Oct 26, 2023, 07:20 PMThe "fern" does perform 10,000 iterations so, regardless of the Basic, it will be slow... But it wasn't too bad...
The 'sphere' was a little slow... but that could be because it was doing the angles 'on the fly'. Storing and reading the angles using an array could possibly speed up the drawing...
Overall, EB performed quite well...
You mentioned that the interpreter was "pretty slow". Logically, that would imply that, you would have had an actual or mental reference for comparison? Regardless, I think it is pretty cool that there are those who are clever enough to create interpreters! Very cool.. (Note: A not very well hidden compliment... Well done!)
The way I am parsing the code is just really sub optimal. The main focus was getting it to run stable. It means a lot to me that you like it. Thank you.
Quote from: ZXDunny on Oct 30, 2023, 09:53 AMTried testing the sphere in SpecBAS but it wouldn't run.
Took a while to figure out but it's because you guys (and QB et al) re-evaluate your STEP expressions (at the very least!) every loop in FOR?
In mine the STEP expression is evaluated once when the loop is set up, so of course the example code failed to run.
Hmm it never occurred to me that would be non standard. It isn't something I use quite often.
I adapted the sphere example from this Rosetta Code entry for Sinclair ZX81 BASIC on the Draw a Sphere task.
10 LET I=21
20 LET J=2
30 FOR K=-PI TO PI STEP 0.07
40 PLOT 21+I*SIN K,22+21*COS K
50 PLOT 21+21*SIN K,22+(I-1)*COS K
60 NEXT K
70 LET I=I-J
80 LET J=J+1
90 IF I>0 THEN GOTO 30
That version should work on your BASIC with little to no modification.
My adaption has a few differences, but works directly with QBasic and GWBasic. That's probably why I assumed there was nothing unusual. I could have left it the way it was and just replaced the PLOT to PSET, but I am trying to take advantage of any opportunity to demonstrate language features.
Honestly, I kind of like the feature. It isn't something I see in a lot of other languages now that you mention it. I think some languages don't even support expressions at all for the step.
I really don't even know what it is that I am doing. Just poking around the dark trying to figure things out based on how I expect things to work.
With my new job, my posts and updates will be few for a while. Hopefully in the next month or so, I will start making time on my days off to get right back into it.
Quote from: Lucidapogee on Nov 03, 2023, 05:01 PMHonestly, I kind of like the feature. It isn't something I see in a lot of other languages now that you mention it. I think some languages don't even support expressions at all for the step.
It's something I never considered myself to be honest! On mine (and in Sinclair in general) the loop point is the statement after the FOR statement, we never go back to the FOR - the bounds are tested on NEXT. I'm assuming that in yours you re-interpret the FOR statement each loop, hence you could use expressions for your STEP parameter. I do of course allow those (you can use an expression anywhere a numeric value is expected) but they only evaluate once.
That makes me wonder. How exactly do you not return to the FOR? Do you maintain a stack or something?
That way I am doing it is the NEXT searches up for the nearest FOR with a matching index variable. Then pulls the parameters and reevaluates. This method is surely slow. But still, I am proud of it. Wasn't easy to figure out. Took me way too long. ;D
Quote from: Lucidapogee on Nov 05, 2023, 05:19 PMThat makes me wonder. How exactly do you not return to the FOR? Do you maintain a stack or something?
Sinclair BASIC stores the loop info with the variable - basically, all numeric vars can store extra information alongside the value they hold. Keep the loop location (the statement after the FOR), the limit and the step inside the var, then when NEXT comes along you test it there.
I would probably never have thought of that. Maybe in tbe future, I will try that method.
Updates!
https://lucidapogee.com/forum/viewtopic.php?t=93
https://lucidapogee.com/forum/viewtopic.php?t=99
There now a DOS Edition (8088 compatible), DPMI Edition (386 compatible), Win32 Edition, and Linux Edition. Still alpha version for now.
The Linux Edition has been tested on various distros.
The DOS Edition has been tested on the Book 8088.
The DOS Edition is compiled with Borland Turbo C 1.
The DPMI Edition is compiled with DJGPP.
The Win32 and Linux Editions are compiled with Open Watcom.
Each edition has almost identical source code and the examples are totally identical. This makes Express BASIC a fully cross platform programming language.
The Linux Edition provides an elf file. I'd like to know how it works on other distros. Let me know.