Immediate Mode

If I run sbrandy.exe with no program specified on the command line, it enters Immediate Mode as expected. But if I specify a program on the command line, and an (untrapped) error occurs during execution, it exits the process altogether (so, for example, I cannot use Immediate Mode to help with debugging, e.g. by PRINTing variable values). Is that expected? Is there a way to make it enter Immediate Mode if an error occurs?

Edit: Forget it, I found the answer myself: I need to specify -chain progname not just progname. Not sure that's intuitive, but it works.

Comments

  • Use
    sbrandy -ch programname
    

    This CHAINs the program, dropping to immediate mode on program exit (unless QUIT or *QUIT is encountered). This is also true for the "full fat" SDL build.
  • Soruk wrote: »
    This CHAINs the program, dropping to immediate mode on program exit (unless QUIT or *QUIT is encountered). This is also true for the "full fat" SDL build.

    Yes, I found it myself (I edited the post, but evidently you didn't see the edit).

    What's the thinking behind this behaviour? None of my BASICs work that way, they always enter Immediate Mode on an error; the only way to exit the process is with QUIT (or equivalent). When might one want to quit the process just because an error occurs, throwing away any diagnostic information?
  • What's the thinking behind this behaviour? None of my BASICs work that way, they always enter Immediate Mode on an error; the only way to exit the process is with QUIT (or equivalent). When might one want to quit the process just because an error occurs, throwing away any diagnostic information?

    It's inherited behaviour from upstream, and it follows other UNIX scripting languages that exit once the script (program) exits, and is behaviour I have made use of in scripts at work.
  • Soruk wrote: »
    It's inherited behaviour from upstream, and it follows other UNIX scripting languages that exit once the script (program) exits, and is behaviour I have made use of in scripts at work.
    I see, not being familiar with Unix I wasn't aware. I am surprised that it hasn't been drawn to my attention before, especially since mine have worked the same (incorrect) way ever since BBC BASIC for MS-DOS some 35-or-so years ago! It's probably significant that, according to my web logs, the total number of downloads of bbclinux.zip is... wait for it... zero!!

    I notice that you also have a -quit <prog> option, how does that differ from just specifying the program to run?
  • It's about time I upgraded my copies of BBCSDL and BB4C! The flag that determines whether or not the interpreter exits by default is set somewhere in (I think) brandy.c, the -quit option allows you to explicitly say you want it to quit irrespective of how it has been compiled.
  • Soruk wrote: »
    It's about time I upgraded my copies of BBCSDL and BB4C!
    I wouldn't bother.

    When brandy quits on an error, what is the exit code set to: the error number?
  • Soruk
    edited March 2023
    Currently, any exit due to an error has exit code 1, but it could make sense to change that to ERR+1 - why +1? Exit code 0 is success, and returning 0 for an untrappable error would not be good...

    Edit: I had forgotten, a program can also set its own exit code as a parameter to QUIT, which if not supplied, is zero.

    Edit 2: I've gone ahead and implemented this. It'll be in tonight's nightlies.
  • Soruk wrote: »
    the -quit option allows you to explicitly say you want it to quit irrespective of how it has been compiled.
    I can't change the way my BASICs work by default because that would introduce major incompatibilities, particularly with IDEs which rely on the 'stay in the interpreter' behaviour (both to make 'immediate mode' work and when debugging).

    But I will look into the possibility of adding a -quit switch which would override the default behaviour, and instead quit in the event of an untrapped error occurring or exiting to immediate mode.

    If I do, it will only be in the Console-mode editions. Personally I don't think it's right to quit-on-error in the GUI editions because you don't see any error message displayed, the app just quits unexpectedly with no indication why.

    Try specifying in the brandy.exe command line a program which fails immediately, because of an error in the first few lines. I don't think you'll like the effect!
  • Just to note, there's a convention in Unix shells to return 128+exit code. Might possibly be less confusing than an off-by-one approach?
  • I understood the exit code to be 128+signal, so a SIGSEGV causes an exit code of 139 for signal 11.

    I could do an off-by-256 or off-by-1024?

    Regarding Richard's possibility of adding '-quit- to his console version, that would certainly make it useful as a scripting language host that could then run a job written in BASIC via Cron, a web CGI etc, where the script is expected to exit upon completion. Also, to that end Matrix Brandy will, if it encounters a line beginning with #! at the start of the file, it is completely ignored and processing continues from the next line.
  • BigEd
    edited March 2023
    Yes, sorry, signal value + 128. But it does seem better to me to add a nice power of two. (Edit: one is a power of two but I mean a larger one!)

    Making BBC Basic more readily usable for scripting sounds like a good thing.
  • Soruk wrote: »
    Regarding Richard's possibility of adding '-quit- to his console version, that would certainly make it useful as a scripting language host
    It's perfectly usable for scripting now, it's an application type that I advertise and use myself. You just use QUIT in your BASIC script if you want to exit the process rather than drop into immediate mode. Insisting that it quits simply as a result of 'falling off the end' is an artificial requirement that is at most just a matter of convenience.

    But of course there's no point in ever using my BASIC in preference to yours in such an application. Those few advantages which my interpreter has over Matrix Brandy are all related to the GUI features (such as the ability to output proportional-spaced text or anti-aliased graphics).

    As far as I'm aware, with the extensions you have added, the console version of Matrix Brandy does everything that the console versions of my interpreter do, if not always in a fully compatible way. Given that yours is also much faster, it's a no-brainer that it would be used in preference to mine.
  • Except structures :D
  • I should add that my interpreter requires any file specified on the command line to be in BBC BASIC 'internal' (tokenised) format, it cannot run a plain-text file specified that way. That means any scripting application will require a tokenisation step, which could automatically append the QUIT.
  • Soruk wrote: »
    Except structures :disappointed:
    As RISC OS enthusiasts (and others that dislike my BASICs) are keen to point out, most things that structures can do can also be achieved using indirection, if not as conveniently or safely. Hence I included them in the "if not always in a fully-compatible way" caveat.
  • BigEd wrote: »
    Yes, sorry, signal value + 128.
    The only value of ERR which really causes an issue is zero, which is reserved for 'fatal error' in BBC BASIC but 'normal exit' as far as the process exit code is concerned.

    Because (in my BASICs, at least) this is already handled entirely differently from the other errors - because it is non-trappable - it seems to me that it can perfectly reasonably be treated as a special case in respect of exit code too.

    So the approach I will adopt, if I implement it at all, will be to set the exit code to the ERR value (which seems the most obvious and natural thing to do) for all trappable errors, but to (probably) 256 or 512 for 'fatal' errors.
  • There's a wee complication, exit codes are the least significant byte of an int, so only values 0 <= ERR <= 255 are possible. 255 seems the most obvious choice then for ERR=0. However, some RISC OS-originated errors have ERR > 255.
  • Soruk wrote: »
    There's a wee complication, exit codes are the least significant byte of an int
    Not in Windows they aren't, I can set an exit code of 256 or 512 without any difficulty. If Linux has that limitation it's unfortunate, but not my problem!

    Incidentally ERR = 255 is 'Unknown error' or 'Unsupported' in my versions of BASIC, and is for example issued when attempting to use EXT# as an l-value in BBCSDL.
  • Not in Windows they aren't, I can set an exit code of 256 or 512
    In the recently released version 0.42 of my Console Mode editions, the following applies if (and only if) -quit <bbcfile> is specified in the command line:
    • Terminating the program with END or STOP sets the exit code to 256.
    • A 'fatal' error (ERR = 0) sets the exit code to 512.
    • Other (trappable) errors set the exit code to the ERR number.
    In all cases (except END) a message is printed to stdout before exiting.

    It's possible that in Linux the 256 and 512 codes will be seen by the shell as zero (seems pretty stupid to me) but there must be a way of getting at the 'real' number because the return value of main() is int not unsigned char!
  • From the man page for exit(3):
    SYNOPSIS
           #include <stdlib.h>
    
           void exit(int status);
    
    DESCRIPTION
           The  exit() function causes normal process termination and the value of
           status & 0377 is returned to the parent (see wait(2)).
    

    It seems to be something buried deep in the system, and a quick Google search doesn't pull up any other UNIX offering exit codes outside the 0-255 range. It's why my tweak to reflect ERR in exit codes uses 255 if ERR=0, as to me it doesn't make sense to use a value that could be interpreted as success for a fatal untrappable error.

    In Matrix Brandy, you can pass an exit code to QUIT, without one it will exit with 0. Similarly, when a program is run with -quit specified or implied, END will also result in exit code 0 but STOP (untrappable ERR=0) will result in exit code 255. Under Windows using STOP to end a program reporting an error condition and END (or QUIT) for a success condition is nicely portable - but since you've chosen exit code 256 this is unfortunately not portable to Linux or other UNIX-derived systems.
  • [Richard Russell]
    edited March 2023
    Soruk wrote: »
    From the man page for exit(3)...
    Possibly so, but I don't call exit() I do a return n;, which returns n from main() as an int (usually a signed 32-bit number).

    Presumably the purpose of exit() is to allow you to abort a process at a point when the stack has not been unwound, for example inside a loop or function, when return would not have that effect. I never need to do that.

    It seems I was right about being able to access the full 32-bit value in Linux, albeit that it's not very straightforward.
    to me it doesn't make sense to use a value that could be interpreted as success for a fatal untrappable error.
    But the alternative is to return a code which corresponds to a trappable error with a specific (and different) meaning from 'fatal error'. All codes from 1 to 255 either already are, or could be, used for such errors. So it's just as potentially misleading.

    It seems to me eminently sensible to return a code which is distinct from 'success' (0) and all the possible trappable errors (1-255); 256 or 512 fulfil that requirement. That they can't be easily distinguished from zero in a Linux shell doesn't worry me, so long as they can be in Windows (trivial) and with some effort in Linux.
    STOP (untrappable ERR=0) will result in exit code 255.
    In my versions of BBC BASIC (right back to the Z80 version, I think) STOP isn't an error, untrappable or otherwise; it doesn't affect the value of ERR. STOP is much more similar to END in the way it is handled internally.

    The reason for this is that STOP is principally of value when debugging, so you want it to change the global state as little as possible. That's why it doesn't close any files, nor change the value of ERR.

    I think this reflects what the BBC decided should happen way back in 1981, but isn't how Sophie implemented it. Even in the original BBC Micro User Guide it says "... STOP prints the message 'STOP at line XXXX' on the screen, otherwise the effect is identical to END".
  • Fair enough about STOP.

    Being able to extract the full return code is only possible, from reading that Stack Overflow thread, if your process is called from another one where you can do that extra work. From the shell, or a shell script it won't be possible, be it Linux, Raspberry Pi or a Mac (unless Apple are ignoring POSIX on this), only on Windows will it work.
  • Soruk wrote: »
    Being able to extract the full return code is only possible, from reading that Stack Overflow thread, if your process is called from another one where you can do that extra work.
    So, should you need to do it, write a wrapper app. Just about the only situation I can think of in which you might want to is if the interpreter is called from an IDE, which can launch a program and then report whether it failed with an error or ran to a successful conclusion. In that situation the IDE could itself be the wrapper app.

    Until about a week ago nobody had ever suggested to me that it might even be useful to quit from BBC BASIC on an error, let alone provide a mechanism for discovering what that error was! That's despite me having made available BBC BASIC interpreters that can be run from a command line for something like 40 years!

    In almost every circumstance I can imagine it's actually better to stay in the interpreter (in immediate mode) than to quit, because far more diagnostic information on the cause of the error is available. You can interrogate the line number, print variables, check memory usage, experimentally call PROCs and FNs directly etc., none of which is available once the process has exited.
  • I guess it rather shows (again!) that I'm coming at this from the point of view of a Unix/Linux sysadmin who does scripting for a living :smiley: And yes, there is a mobile network here that has some scripts, called by cron, that are written in BASIC and run in Brandy. If course when developing them I would be using the -chain (or -ch) option for it to stay within the interpreter on error, but the last thing I want it to do is hang in the interpreter, especially when run non-interactively, if something goes wrong. I capture the output (it any) to a log file, which can be examined for clues before I did back into the code to identify what went wrong.
  • [Richard Russell]
    edited March 2023
    Soruk wrote: »
    the last thing I want it to do is hang in the interpreter, especially when run non-interactively, if something goes wrong. I capture the output (it any) to a log file, which can be examined for clues before I did back into the code to identify what went wrong.
    This is all true but in practice you are probably going to want to log more than just an error code: at the very least the line number, and perhaps a variable dump etc. So you will be trapping the error with ON ERROR so you can log this information, and once the error is trapped you can exit the process using QUIT.

    This is precisely why I've never considered automatically exiting the process on an error to have any significant value: you have always been able to achieve this trivially by inserting ON ERROR QUIT ERR at the start of your program! If you really want a command-line option, you can do this:
          IF INSTR(@cmd$, "-quit") THEN ON ERROR REPORT : QUIT ERR
    
    To me, using BBC BASIC code to determine whether or not your program exits on error is far more in the 'spirit' of the language than some command-line incantation. This will continue to be the only way to do it in BB4W and BBCSDL.
  • Soruk wrote: »
    I'm coming at this from the point of view of a Unix/Linux sysadmin who does scripting for a living
    Indeed, and I'm sorry if my comments came across as arrogant. I acknowledge, of course, that you are by far the better programmer (even if your 'day job' is mostly script programming). I have always recognised and admitted my failings as a coder, how could I do otherwise when the extreme slowness of my interpreters is obvious to all? :/
  • I certainly wouldn't call myself a better programmer (if I was, I'd have got structures working already!), and what you have with your code is nothing short of amazing.

    Indeed, just yesterday I've been playing with some historic BASICs of yours now that I have PiTubeDirect running on my Master 128, both Z80 as ROM and under CP/M, and for the Master 512.
  • Soruk wrote: »
    if I was, I'd have got structures working already!

    Structures aren't complicated. Creating them is more work than accessing them, since the latter largely shares the same code used to lookup ordinary variables (i.e. scanning a linked list). The only significant difference is that whereas in the main variable lookup the 'pointer' field is the absolute address of where that variable is stored, in the case of a structure it's the offset from the start of the structure.

    Creating structures is a fair bit of work, but is very similar to creating arrays. The main difference is that (in my BASICs at least) arrays have a single pointer - the data always follows immediately after the descriptor - whereas structures have two pointers - one to the descriptor and the other to the data.

    As far as I remember, I developed structures by initially creating them with BASIC code and then getting the code to read and write them working. When that was working I added the code to make them LOCAL and pass them to functions. Only when all that was working did I extend DIM to create them.

    I found that staged approach made the whole thing much easier.