What to check when writing in BASIC?
Pages: 1 2
Julie Stamp (8365) 474 posts |
What habits do you try and keep when writing in BASIC? Could they be checked for by a program? What about tedious mistakes? Here’s an example I thought of. Problem: Unknown or missing variable Example:
Rule: Every variable name in an expression must appear somewhere in an assignment. |
Rick Murray (539) 13850 posts |
I think StrongBS used to be able to check for that sort of thing (as it would have reduced customer_name$ to something like `a$ but would not have any match for customrename$). Anybody that has the DDE and doesn’t write weird code could use ABC for the same end, it’ll throw errors, which is much less hassle than trying to remember to exercise all of the parts of a program to see if it crashes). It’s also one of the reasons I like C. If the coffee compiles, it is valid. It may not be right (and frequently isn’t!), but it does remove the entire category of lurking syntax errors that can crop up in BASIC. |
Rick Murray (539) 13850 posts |
For what it’s worth, when I used to write code for Visual Basic, I set the environment to require all variables to be declared before use (like in C). Which meant there was no possibility of weird things happening because a typo stuck half the results into an entirely different variable. If it hadn’t been declared, it would be faulted in any context. I really would have no problem at all if such an option was available to BASIC (hey, we could resurrect the |
Rick Murray (539) 13850 posts |
A problem with BASIC is that it doesn’t have a formal definition. Nemo posted this, valid code: DEFFNfourargs(A%,B%,C%,D%) DEFFNthreeargs(A%,B%,C%):LOCALD% DEFFNtwoargs(A%,B%):LOCALC%,D% DEFFNonearg(A%):LOCALB%,C%,D% =A%+B%+C%+D% I haven’t tested this (lying in bed trying to pretend I won’t be getting up at six tomorrow morning) but I’ve used something similar: DEFFNgive_me_a(type$, val) LOCAL int% CASE type$ OF WHEN "string" : =STR$(val) WHEN "istring" : =STR$(INT(val)) WHEN "float" : =val OTHERWISE : int% = INT(val) =int% ENDCASE =0 This is part of the reason 1 why creating a compiler for BASIC is hard. 1 The other part is EVAL. |
jan de boer (472) 78 posts |
i really hate to promote my own software, but !reggraph which is a BBCBasic editor with utilities like program crunch, graphical assembler register usage display, also has some crude program check options. For instance, number of (), {} and [], CASE/ENDCASE balancing, number of subroutine parameters, etc. Not a proper BNF yet. If one sets Menu on Main window to Global xref, it’s possible to global-xref on never-written-to or never-readout variables or var’s whose number is below some threshold e.g. once (= less than 2). This is, of course, nice to spot typing errors. Local Xref is a good method to decide which variables should be LOCAL, also preventing errors. Alas, for now, Readme files are Dutch only, life’s too short.. btw its here: www.tellima.nl/riscos/ |
Paul Sprangers (346) 525 posts |
There’s some pretty impressive coding, Jan! |
Paolo Fabio Zaino (28) 1882 posts |
@ Julie I have refreshen my BBC BASIC on the last year or so, anyway: 1) Fake Local variables, when a variable is declared as LOCAL it’s not LOCAL to a function, it’s LOCAL to the entire call stack from the point of declaration. This may have had sense on a BBC Micro, with like 32KB of RAM, but it makes no sense on any RISC OS machine (since the A310 probably). Generally, people tend to use (and re-use) variable names in multiple functions. So, with the problem above, we technically have the exact issue every language has with global statements, a variable may have its value changed in a separate function and create side effects.So, my habit in this case is to use a short prefix before any local variable’s name, something like xyz_a% where usually xyz are the initials of the relative function name. This helps to avoid the issue described above, but in all honesty I think BBC BASIC behavior should be fixed and a LOCAL should be LOCAL only to the function. we could add a new keyword called SHARED if we want a variable to be shared with all the functions in the relative call-stack. The xyz_ for LOCALs can be checked by a simple RegEx based program. 2) Blocks, another “ghost” from the past. They were useful long time ago, but structs are way more human readable and less prone to bugs. However, while we wait for those, I use variable names to identify fields in a block quickly: 3) I agree with Rick, when a var has not been defined yet, BBC BASIC should give an error as currently does. 4) Another habit of mine is always to use a BBC BASIC compiler to check the entire program for all sorts of errors that the interpreter won’t check given its nature. I normally check the code using both RiscBASIC compiler and ABC. Yes, I am fully aware they do NOT support the full BBC BASIC V syntax (although RiscBASIC seems quite good tbh, definitely way superior to ABC). This practice helps speeding up coding and has also other advantages. 5) I have added to my list of things to do a full BBC BASIC code Linter, but I haven’t started it yet, the usual lack of time. I remember someone wrote a Lint-like utility for BBC BASIC, but I can’t recall who and the name of the utility. |
David J. Ruck (33) 1636 posts |
At the risk of hijacking the thread I’d like to say how impressed I was with Jan’s !Nonogram Having written my own solver (versions in C, C#, Python and Perl) and coincidental using a compatible file format, I couldn’t believe how much faster this RISC OS program was. While the program is mainly BASIC, the heavy lifting is done by some impenetrable ARM assembler. The !Help describes an algorithm pretty similar to mine, but either I’ve missed a trick or his is implemented a lot better. I’d love to see the equivalent code in a high level language, even BASIC. I should also mention that there is another RISC OS implementation with the same name by Steve Simpson, written in C – and also quicker than mine :( but slower than Jan’s. |
Frank de Bruijn (160) 228 posts |
Bad idea. That could break existing software all over the place. |
Paolo Fabio Zaino (28) 1882 posts |
That is what the keyword “SHARED” is meant to fix ;) But, if fixing problems with BBC BASIC is a problem, we can always build a clone with another name and with no issues from the past, and people can choose which one they want to use for their code. |
Steve Drain (222) 1620 posts |
But what is an assignment? You will not get far just looking at And there is the way a local variable ensures that a global equivalent exists. Fun, isn’t it? |
Steve Drain (222) 1620 posts |
If it distingushed all the variety of ways of creating variables and then checked expressions. Maybe it did. Crunchie only crunches ‘names’ wherever they occur, but it can generate a report of all that are found with a tally. So analysis of that will show you ‘names’ with only one use. You can do more in conjunction with Reporter. Still, this is not true profiling. |
Steve Drain (222) 1620 posts |
Perfectly good BASIC code can fall foul of ABC. ABC BASIC is not BBC BASIC. ;-) |
Steve Drain (222) 1620 posts |
True. ‘Like Topsy is just growed’ I think. However, see the reply I will give to Paulo, below.
Sophie herself posted such a routine to the Newsgroups (remember those) that dealt with arrays with multiple dimensions in this way. I have used that construction for years and have an assembly macro library that may have 32 entry points per function. ;-) That is excessive, but it is rather specialised. |
Steve Drain (222) 1620 posts |
Using an early exit from a routine (PROC/FN) is legitimate and is a very good way to implement an |
Steve Drain (222) 1620 posts |
Fake? Giving variables scope in sub-routines is not unknown outside BBC BASIC. It is is the way it is done and you do not have to exploit it. Indeed, I quite rarely do it now with fast processors, and pass all variables as parameters. I do have some routines that are only called inside one other where I do not bother, on the basis that it will be a tad faster. The way BBC BASIC deals with variables is very simple and will be one of the reasons it runs quickly. Perhaps more fun, with reference to Julie above, is this snippet: A%+=1:PRINT A% PROCtest a%+=1:PRINT a% END DEFPROCtest:LOCAL a%:ENDPROC Run it with and without |
Rick Murray (539) 13850 posts |
I understood that he was referring to the change in behaviour breaking loads/many/all existing apps. The rule is very simple. When you have potentially thirty years (plus!) worth of software, you augment and extend existing behaviour, you don’t alter it. If you need to introduce something new, have an option.
You and who’s army? The list of “stuff that really needs done” is growing ever longer as the rest of the world runs blindly forward and we… maybe crawl sideways a little.
Yup. It still can’t handle
Yup. I absolutely want to stick a knife in the back of whatever cockwomble stated that “proper” functions have only one exit point. The way I write test-something functions is that I perform tests in expected decreasing likelihood. If any test fails, the code bails out of the function then and there. The alternative is an unholy mess if nested IF statements designed to wrap around this artificial nonsense, or, worse,
Indeed. And not only that, but local variables are local. That means they exist purely within the function or code block that created them and, more importantly, are not accessible to subfunctions. Here I’m talking about C. BASIC’s variable scope rules are peculiar. To highlight, I’ll finish with:
|
Steve Fryatt (216) 2105 posts |
It’s not quite the same test, but using Tokenize with the
Whilst the example is in a Bash shell because that saves me swapping keyboards, the version linked to above is the native RISC OS CLI one. |
Paolo Fabio Zaino (28) 1882 posts |
StrongEd/Zap → Replace ALL “LOCAL” with “SHARED” (works also on crunched files) = solved. One could also use another keyword for the actual true LOCAL variables, but then things may get confusing. BTW, I am pretty sure that BBC BASIC for Windows (and probably also MATRIX BASIC) have already fixed that. bbcbasic.co.uk also report this: Anyway, given the language has so many issues from the past, the more I think about it and the more I think it’s time for a new Interpreter and leave the old BBC BASIC for nostalgia. Just my 0.5c |
Paolo Fabio Zaino (28) 1882 posts |
? There are already “clones” http://brandy.matrixnetwork.co.uk I actually have written a BASIC interpreter for RISC OS/AmigaOS/AtariTOS, which also had support for OOP (the way I could implement that – like 25 years ago -, but still). I never finished support for SWI calls (I wanted to have it abstracted a bit, but it shouldn’t be too much work). The syntax is not in common with BBC BASIC (it’s more on the side of Visual BASIC). I may give it a go and see how it works on RO 5, I just don’t dare looking at code of mine so old! But maybe after few beers I may get the courage ;) |
Steve Drain (222) 1620 posts |
I think we can all agrree that structures are desirable. If you wanted them you might have used Basalt or a library, though. Using variables for offsets is expensive and eats up the single namespace, unless you use Hungarian notation, as with you xyz_. One thing you might do is translate those variables to constants, which Crunchie and other utilities can do. Here is a fun trick that I learned from nemo and slightly elaborated: DEFPROCwimp_icondata LOCAL O%,P%:O%=&8200:REM input buffer [OPT 6 .icon_text :REM =icon_sprites 12 bytes .icon_sprite :REM =icon_sprites 12 bytes .icon_buffer :REM =icon_sprites .icon_sprites :&0:REM .icon_valid :REM =icon_isname .icon_isname :&0:REM .icon_bufflen :&0:REM ] ENDPROC With a little more elaboration a |
Paolo Fabio Zaino (28) 1882 posts |
I like Basalt, the only thing I do not like is that it’s not in ROM and universally installed. So, it becomes a dependency (which btw is not even available in PackMan). So too many moving pieces.
Yes I got to similar conclusions and got similar set of tricks, but that requires ASM knowledge and it’s already starting to hack around the language. My points were in reference of cleaning up the original language. I guess some people prefer less formal coding, but that also means longer debugging time. |
Steve Drain (222) 1620 posts |
That would be me a couple of years back, I expect. I was writing a formal definition of BASIC, with some utilites to test it. I was pretty happy with nearly everything except the expression definition, which I could not get to cope with those corner cases I knew about. Jeffrey will know what I mean. If you want to see it download Parser I shall not be doing anything more with it, so treat it how you like. |
Steve Fryatt (216) 2105 posts |
To tidy up the loose ends on the variable tests…
or…
It won’t spot double-typos: if you assign a variable twice and read a variable twice and get one of each wrong, then that will slip through the net. I’m not sure whether that could be detected sensibly anyway. Because Tokenize is doing a “proper tokenisation”, and that requires knowledge of whether a variable is on the left or right of an expression, it’s relatively easy to total up the numbers of “reads” and “writes” for each variable name that’s encountered. |
Steve Drain (222) 1620 posts |
Crunchie also detects left/right, but it does not take account of that for its tally. I suppose it could, but I am unlikely to go that far when there are alternatives. |
Pages: 1 2