Hacking in BASIC
Steve Drain (222) 1620 posts |
I have just realised that I have made a mistake. My example should have included IF <cond1> THEN ... ELSEIF <cond2> THEN ... ELSEIF <cond3> THEN ... ENDIF Sorry. :-( |
nemo (145) 2546 posts |
It may surprise you to learn I think of little else. Yes, bytes != codepoints != graphemes != chrs != grapheme-clusters. |
nemo (145) 2546 posts |
Steve claimed
Not without further guidance. Your example is already valid Basic. Even if you came across an ENDPROC after your single ENDIF you’ve not ascertained whether your truncated syntax is in use. You could only be sure if you reached the end of the program. PROCone(TRUE) :REM produces "Peekaboo, number 2" END DEFPROCone(A%):LOCALB% IFA%=-1THEN B%=2 ELSEIFA%=1THEN PRINT"One" ELSEIFA%=2THEN PRINT"Two" ENDIF PRINT"To which clause does this belong?" ENDPROC REM Basic syntax is not to be sneezed at DEFPROCtwo(B%) ENDIF ENDIF PRINT"Peekaboo, number ";B% ENDPROC You’d need some way of marking that ENDIF as a catch-all ENDIF. |
nemo (145) 2546 posts |
I know. I said that yesterday when I wrote: DEFPROCweird_but_legal(global%) CASEglobal%OF WHENFALSE:LOCALa%,b%,c% OTHERWISE:REM global vars will be used ENDCASE a%=b%+c% ENDPROC It might be possible to work around it, like only pushing a plain encapsulation if there’s already an ERROR-catching encapsulation (so that CASEs embedded within the TRY don’t become TRYs themselves), whereas the plain CASE in the PROC example wouldn’t need one and so would behave as it always has… I’ll have a think. |
Rick Murray (539) 13840 posts |
It’s a shame DavidS is not around. This entire thread should serve as an example of why a BASIC compiler would be dead on arrival. One can make a compiler that compiles legitimate BASIC (like ABC, etc), but since BASIC has so many quirky edge cases, it would be nothing short of a nightmare supporting them all, so the compiler will at best be “like BASIC” but not “BASIC”. And this isn’t even counting the issues with EVAL! |
Steve Drain (222) 1620 posts |
During translation you count the number of If you wanted to implement this directly then it is indeed more tricky and I thought you might rise to the challenge. ;-) |
Martin Avison (27) 1494 posts |
On my list of things to try is to get Reporter to do that automatically at the point of the error. |
Chris Hall (132) 3554 posts |
One can make a compiler that compiles legitimate BASIC (like ABC, etc), It is a pity that RiscBASIC was not 32-bitted. That did compile legitimate BASIC. ABC does not (and never has). |
Alan Adams (2486) 1149 posts |
Oh, yes please. (Even if I have managed to crash reportstack occasionally, and Martin has corrected it when I manage to provide a reproducable example. At least this discussion explains WHY it has been so tricky to implement.) |
Alan Adams (2486) 1149 posts |
And while we’re talking about error handling, I got this a few minutes ago. I’ve never seen this one before.
|
Steve Drain (222) 1620 posts |
I have been contemplating this and I think I have a way to modify BASIC to do it, but it cannot be done by Basalt, which would have to insert enough of At the moment, if during program execution an I propose that if This is rather making |
nemo (145) 2546 posts |
What an odd thing to say. And completely incorrect. It is possible to write a compiler that is 100% compatible with BBC BASIC in all her eccentric glory. But ABC certainly isn’t it.
Which existing ENDIF? You can’t be claiming that ALL nested multiline IFs end at the same ENDIF, so when you reach the first ENDIF how do you know there isn’t another one?
This is the problem – ELSEIF is not new syntax, it already exists and already works fine thank you.
It’s not ‘tricky’ it’s incompatible with existing use. Martin wished
It would be easy enough to give Basic a hook at the point it’s about to call the error handler. To clarify: The majority of Basic “errors” aren’t actually errors. That is, they don’t get Generated(OS_GenerateError) or go through ErrorV. So there is no hook. (There are a couple of exceptions but I’d have to look them up). |
Steve Drain (222) 1620 posts |
You have to assume the co-operation of the programmer, who desires an In that case there will be at least one
The method I outlined for BASIC itself would not require co-operation, but I can see the flaw when applied to an old program that happens to satisfy the new syntax, and results in a logic failure. So, yes, there needs to be something to signal the new use, but cannot interfere with |
nemo (145) 2546 posts |
|
Rick Murray (539) 13840 posts |
It’s perhaps worth mentioning the old school method… TRACE TO "filename_goes_here" TRACE PROC
Including the things that are being mentioned here, like the DIM LOCAL TRUE thing?
ABC compiles legitimate BASIC. It just doesn’t compile much legitimate BASIC and some things (hello local variables) come with a pile of quirks. This is a known known. :-)
It is possible, yes. But what is technically possible is not what is practicable. Or reasonable. Or even likely. Speaking of ABC, I’ve given up on ever getting access to the source code. What’s it been, three? four? years or so I’ve been on about this? Maybe more. You’d have thought it wouldn’t have taken too much time to be like “here, sign this NDA and see if you can make this better”. But alas…
It would be nice, to change this: IF something THEN Blah ELSE IF something_else THEN Meh ELSE IF third_time_lucky THEN Wah ENDIF ENDIF ENDIF Into something more like: IF something THEN Blah ELSEIF something_else THEN Meh ELSEIF third_time_lucky THEN Wah ENDIF |
Steve Drain (222) 1620 posts |
Are you sure? My tests throw up a Syntax Error. Apart from |
Martin Avison (27) 1494 posts |
Oh yes, Reporter already uses that for some facilities (eg reporting whenever and where a variable is changed) – as well as an extended TRACE to Reporter. |
nemo (145) 2546 posts |
Rick said
You have a specification? I’m sure there are holes in the average user’s knowledge of Basic, but the implementation is definitive, and not actually very complicated. It’s basically a BBC Micro program remember.
It would be major work to “not quite do what BASIC does” too. It’s merely a question of intent, not possibility. It’s an awful lot of typing whatever you choose to implement… so you may as well implement what it does instead of what you might think it may do. For example, CASE is now used for much bigger things than I think it was intended for. In desktop programs it is typically used to select large sections of code via integer constants. Because that results in huge sprawls of code and poor performance we tend to replace the chunks of code with PROCs unless they’re very small (which introduces another bottleneck). Either way, in a sensible language you’d have an array of function references indexed by the integer. Or, if your language has them, an associative array. Basic however only has CASE, and this requires not only skipping over every single line in the whole CASE statement, but also evaluating (on average) half of the constants every single time, and converting them to a float because CASE doesn’t actually use integers. This can be improved, but requires a simple form of static analysis: The type of the CASE variable selects between two possibilities (three, if you add the extension I knocked up yesterday) – strings, or numbers (or “truthy” booleans). In each case, every WHEN holds a value that may be a constant, in a fixed place, or variable. If it’s a constant (eg 5, 3×20, “String”, LEN"uggh") then that can be evaluated once and stored with a pointer after the WHEN (much like the SCT cache). Addressable values (A%, fred$, HIMEM) can have their address stored instead. Non-constants though (fred%×2, LENa$, b%×c%) must be evaluated as now – but the address of the expression can be stored. The result is a table of 12-16 bytes per WHEN. In this way the CASE can be executed and the appropriate code found extremely rapidly. A tiny change to the stack presence I’ve already added would allow the terminating WHEN/OTHERWISE to jump directly to the ENDCASE without scanning. Similar wheezes can accelerate all flow control code – IF/THEN/ELSE/ENDIF, WHILE/ENDWHILE all involve searching static code for the same thing over and over. The SCT helps, but is overwhelmed by large programs. Enlarging the cache is like trying to empty the sea using a larger teacup. And the perversity of having 54 separate chains for variables yet only two for all the PROCs and FNs in the program really does betray its highly memory-constrained origins. Even without peephole optimisation or removing any of Basic’s memory usage, a “compiler” could significantly improve performance by eliminating all the look-ups and most of the evaluations.
As I wrote yesterday, this is 100% equivalent: CASEOF WHEN something Blah WHEN something_else Meh WHEN third_time_lucky Wah ENDCASE Steve misunderstood
Yes! Precisely! So there is definitely no code out there using that construction. So one could definitely adopt that syntax into Basic without breaking anything. But it’s so ugly.
Yeah, of the trivial LOCALs I added, LOCALTRACE is one of the more useful ones. |
Steve Drain (222) 1620 posts |
Gotcha! We are looking at this from different ends of the telescope. You are are seeing how to rewrite BASIC, I am seeing how to keep BASIC the same but adapt it to have the desired effect. Let’s leave it there. |
Rick Murray (539) 13840 posts |
No, but if somebody is going to write a compiler, they’d better write one unless they fancy p**sing into the wind in the dark.
Ah, yes. A BBC Micro. A machine a tad more complicated than that which gave birth to Unix and C…
Huh? What? FFS. |
Steve Drain (222) 1620 posts |
I have not added anything to Basalt for ages, but I thought I would have a go. The I have also written the real No release yet. |
nemo (145) 2546 posts |
Excellent work! Today I have mostly been putting PROCs and FNs into the alphabetic lists which makes them go faster. I’m still kicking LVAR lovingly in the shins. |
Steve Drain (222) 1620 posts |
That would put an end to my Object Oriented BASIC, which keeps separate class lists of PROCs and FNs for magic manipulation. ;-) |
nemo (145) 2546 posts |
Well it’s the same code after all, and all one has to do is select the appropriate chain from the actual first character, and then use the PROC/FN token as the pretend next character. Plus a little legerdemain with the pesky @ & 1-9. Looks odd in a memory dump but works fine. Doesn’t it LVAR, that’s right, it does. |
Steve Drain (222) 1620 posts |
I build each list, by devious means, from a set of routines in a class library. Once an object’s class is identified the appropriate routine list is linked in temporarily to implement an attribute or method. Because classes have inheritance, one list links to a superclass list. This relies on only one list for each of PROCs and FNs. ;-) |