Implementing OLL

52 views
Skip to first unread message

The Beez

unread,
Sep 29, 2024, 9:12:39 AM9/29/24
to 4tH-compiler
OK 4tH-ers!

Let me warn you, this is a uBasic/4tH story, so if you're not interested - you can stop reading.

Most recently I came across a YT video (https://www.youtube.com/watch?v=A3gTw1ZkeK0) - go check that out if you're curious. This guy built a tiny bytecode like compiler to create some tiny language of his own.

I thought I could do better, so I pulled out my uBasic/4tH interpreter and found out pretty quicky, I could do a version that may compile a little slower, since it requires two passes, but execute a fair bit faster, since it didn't require to figure out the address of the label and used function pointers rather than symbols. I even extended the vocabulary, making it a bit more useful, while retaining the length (~120 lines).

Agreed, I didn't need a stack, since uBasic/4tH already has a stack - so why not use it. So if you're one of these oldtimers who has been with the project for some time, you'll know that around 2009 I announced a Tiny Basic like BASIC interpreter. Which was both pretty tiny and pretty limited. The thing didn't even have a REM statement, which was the first thing I added.

The next one was a CHOOSE statement, the equivalent of what is RND() now. Then came the user stack to compensate for both local variables and parameter passing. Then we added hashed labels - so we could have normal functions names. Built-in functions followed, then true local variables and parameter passing (all by reusing the stack).

Then the big one, strings. It really took me some time to figure that one out. Then partitions and finally error handling. And there we are.

 Note I was always keen to find solutions that didn't violate the original architecture. Labels are still labels - which is numbers. Hashed ones, agreed, but numbers. I found out where I had to add functions by writing a program using some and see where it bombed. That's where I could hook in built-in functions. Since a variable is represented by an address, all kinds of variables are represented by an address. 

If you can properly encapsulate it syntactically and conceptually it's acceptable to the user. Don't break what isn't broken - instead use it and extend it.

That has made uBasic/4tH into a quirky language - but each time I find out how well it works - even when only a limited amount of memory is available to it. OLL is no exception:
  • I can use NAME() to hash any string or label;
  • I can use LINE() to determine whether it is a valid label;
  • I can use TEXT() to find out whether a variable represents a string or an integer;
  • I can combine expressions using SET() and IIF();
  • I have a functioning stack at my disposal anytime.
Consequently, you can easily construct dense code - like C. And it makes it more versatile than I initially thought.

E.g. after that I decided to create a "language converter" - like I did for "Uylhmn n" (to be fair, I nicked a lot of code from that program). To my surprise I could encode every OLL command to a compact uBasic/4tH expression. E.g. this is my "hilow" OLL program converted:

Push Rnd(16384)
Push 10
a = Pop() : Push Pop()%a
Push 1
Push Pop() + Pop()
Push 3
_LOOP
a = Pop() : b = Tos() : Push a, b
Print "Guess a number between 1 and 10"
Input a : Push a
a = Pop() : Push Pop()-a
If Tos() = 0 Then Goto _GUESSED
If Tos() > 0 Then Goto _TOOLOW
Print "Your guess was too high"
Goto _CONTINUE
_TOOLOW
Print "Your guess was too low"
_CONTINUE
a = Pop()
Push 1
a = Pop() : Push Pop()-a
If Tos() > 0 Then Goto _LOOP
a = Pop()
Print "You didn't guess it. It was:"
Print Tos()
a = Pop()
End
_GUESSED
Print "You guessed it!"
End

Unless you have no idea of how a stack works, it's pretty legible. It always surprises me how much punch uBasic/4tH actually packs and how useful it turned out to be after the many enhancements it's had.

All code is in SVN.

Hans Bezemer
Reply all
Reply to author
Forward
0 new messages