So here comes my report on the current state of affairs in the BL project.
0. What I wanted to do and why:0.1. I wanted to de-interleave the GOTO/GOSUB code into SUB's and/or FUNCTION's to eliminate the necessity to count the current stack depth in every program step. Stack depth counting is required to be able to unwind the stack in case the interpreter hits an exception and needs to be reset in its initial state recovering its program stack from the state of deep recursion and/or deeply nested mutual calls. If the stack is left unwound, its uncontrolled growth may lead to its eventual exhaustion and total program crash.
0.2. The existing strategy of stack depth counting however isn't a panacea. Even if the stack is unwound successfully and the interpreter is reset to its initial state, in very many cases there is no possibility to correct the faulty LISP code already entered for execution. If the faulty code is a simple immediate arithmetical expression, it can be re-entered with all the typos corrected and re-run again. But now let's assume that we entered the definition of a complex lambda successfully, the syntax checker hasn't found any lexicographical errors, and the lambda name has been added as a SYMBOL to the symbol hash table. At run time however the lambda may cause an exception e.g. due to improper math calc hardcoded in it. The interpreter will be reset but the lambda cannot be recoded because its SYMBOLic name cannot be redefined in the hashtable. BL doesn't support redefinition of its SYMBOL's. It can be fully reset
only by quitting the current session altogether. It makes the entire headache of counting the stack depth
not worthwhile.
0.3. There are over 550 instances of
BSD (stack depth counter) incrementation throughout the code corresponding to GOSUB calls, and there will be an exact same amount of decrementations to keep the stack balanced. And all this in just one single cycle without going into any recursion or a nested call whatsoever. Going recursive will increase these numbers many times over. It means we will be executing many thousands of incrementations and decrementations over and over again for a slim chance of unlikely error in a piece of known working code. This will be a
speed killer for any interpreter even if it runs compiled to machine code, e.g. like OxyLISP will.
1. What I did:1.1. I thought that natural early bailout from functions in response to an error will keep the stack balanced equally well without any artificial stack depth counting. So I tried to de-interleave the existing GOTO's and GOSUB's into more structured SUB/FUNCTION code entirely. But I was able to do so without breaking the expected program flow only to some two fifths of the total amount of program code. The entire
LispEval refuses splitting into functions no matter what I do because the expected program flow gets broken whenever the error is raised in anything more complex than a simple arithmetical operation.
1.2. I became convinced that adding continuous error flag checks and assigning return values to functions may lead to overhead penalties as severe as those caused by continuous
BSD incrementation and decrementation. The interpreter will stay just as slow with functions as it was with the stack depth counter.
1.3. Based on my findings and considerations as per Items 0.2 and 0.3, I reverted the entire code to its initial GOTO/GOSUB state and
deleted the BSD counter altogether.
In doing so I expect that infrequent typos in the interactive input of simple math one-liners will not eat up the stack to any visible amount while major mishaps like those described in Item 0.2 will require the user's univocal exit from the current session.
1.4. I added a command line option
-q (quit session on error) to let the user to either proceed as per Item 1.3 or force the interpreter to quit the current session immediately on the first error encountered. The option works in both interactive and file-load-and-run modes but seems more practical in the latter.
2. What I'm planning to do:2.1. The initial QB functionality with
BSD can be fully re-implemented in Oxygen
with Charles' ReturnLabel inline asm trick but OxyLISP will suffer speed penalties as described in Item 0.3. We should seriously reconsider these consequences before we proceed that way.
2.2. The initial QB functionality can be fully re-implemented in SBLisp when it is rewritten in CBASIC.
We can use Charles' trick emulated with GCC's inline assembly too but only to suffer the exact same unjustified penalties. GOSUB/RETURN functionality can also be emulated with the aid of GCC's inline assembly.
2.3.
FBSL's DynC does not support inline assembly of its own therefore Charles' trick can't be used in FBSL at all. FBLisp will stay as per Items 1.3 and 1.4 even in its DynC implementation. FBSL can do it all as well in both its BASIC and DynC variants.
2.4.
I suggest we consider the current state of BL code as the final prototype before getting down to the actual implementation in each respective language.2.5.
I propose OxyLISP and O2 as the first step of BL implementation in each language. O2 with its "strongly elastic" arrays is easier to work with than pure ANSI C or CBASIC and OxyLISP will immediately show us what we may expect from DynC and moreover, from fully optimized -O3 GCC, later.3. What I'm currently doing:3.1. I'm currently working in FBSL with FBLisp.
3.2. I've added a
-h option to display a brief usage and option help screen.
3.3. I've changed -d to
-v (verbose) to display extended error messages and LISP code as it is being loaded from the disk file. Without that option, BL displays only loaded SYMBOL names (as Rob said) and short error descriptions as it did before. You can see Rob's ASCII Mandelbrot loaded into my FBLisp without the -v switch in my latest screenshot.
3.4. I've added trig and rounding mode functionality. OxyLISP can use msvcrt.dll API's. If something is missing in SB, I'll add
#unimplemented stubs for the time being. They will be changed for GCC's intrinsics when ported to CBASIC/GCC for final compilation. This way OxyLISP, SBLisp and FBLisp's LISP scripts will fully compatible until/unless language specific add-ons are developed and added in the future.
3.5. When I'm through with pow and division functions, I will post the FBSL sources here for examination (they are easy to read in the two other languages too), and a precompiled FBLisp binary for testing.
Dixi. UPD Charles inspired me to make one last attempt at restoring BL's program stack properly. This time I succeded, therefore the lines that are striken through above no longer apply. BL will handle its stack just as good as Lisp-in-Basic did, but more efficiently and visibly faster.