uBasic creeps more and more toward a modern BASIC

91 views
Skip to first unread message

The Beez

unread,
Dec 22, 2014, 9:20:18 AM12/22/14
to 4th-co...@googlegroups.com
Hi 4tH-ers!

I can't let it go, I have to improve it.. ;-)
I have always wondered if I can make the user stack more transparent and I think I cracked it. All it needed was some more - or changed - commands:

Keywords

  GOSUB label (expr, expr, ..)
  GOSUB label
  PROC label (expr, expr, ..)
  PROC label
  RETURN (expr)
  RETURN
  PARAM (expr)


Functions

  FUNC (label (expr, expr,..))
  FUNC (label)


Note GOSUB en PROC are the same thing. But it now takes an optional parameterlist. These parameters are stored on the user stack. PARAM is like LOCAL, but it also stores the number of items on the user stack in the local variables it creates, e.g.

  PROC _Sub (12, 23)
  End

  _Sub PARAM (2)
    Print a@, b@
  Return


Will print "12 23", which is probably what you expect. FUNC() even goes a bit further. Like PROC you can add an optional parameter list, but it will also return the value you assigned with RETURN, e.g.

  Print FUNC (_Fun (12, 23))
  End

  _Fun PARAM (2)
  RETURN (a@ - b@)


Which will print "-11", which is the subtraction of "12-23". FUNC() was harder to implement than you think. It required a SECOND interpreter to get right. However, seems to function pretty good. It will even do multiple recursion unless you mix it with GOSUB calls. The reason for that is that FUNC() will start a new interpreter each time, where GOSUB doesn't:

Print Func (_Sub1 (3))
End

_Sub1 Param (1)
  Print "Sub1(";a@;")"
  If a@ = 1 Then
    Return (-11)
  Else
    Return (Func (_Sub2 (a@+1)))
  EndIf

_Sub2 Param (1)
  Print "Sub2(";a@;")"
Return (Func (_Sub1 (a@ - 2)))

Prints:

Sub1(3)
Sub2(4)
Sub1(2)
Sub2(3)
Sub1(1)
-11

Just as you would expect. Here is a small program that brings it all together:

Proc _PrintBye (5)

Print Func (_Sub (23, 45))
Print "Hello"

End

_Sub Param (2)
  Local (2)
  c@ = Func (_get1)
  d@ = Func (_get2)
  Print a@, b@, c@, d@
Return (c@ + d@)

_get1 Return (1)
_get2 Return (2)

_PrintBye Param (1)
  Local (1)
  For b@ = 1 To a@
    Print "Bye ";
  Next
  Print
Return


Note that all previous programs should be running just fine without any modifications. I still have to test some stuff, but you'll see it appear at one moment or another in SVN.

Hans Bezemer











The Beez

unread,
Dec 22, 2014, 9:24:27 AM12/22/14
to 4th-co...@googlegroups.com


On Monday, December 22, 2014 3:20:18 PM UTC+1, The Beez wrote:
It will even do multiple recursion unless you mix it with GOSUB calls.

Oops, that should  be "mutual recursion" of course (in Forth: DEFER).

Hans Bezemer

Ron K. Jeffries

unread,
Dec 22, 2014, 10:32:01 AM12/22/14
to 4th-co...@googlegroups.com

Dr. Beez,

You continue to amaze & delight your fan club. ;)

A philosophical question, if you don't mind:

Besides the always valid reason of "because I can" what do you see as the possible future of your ever-improving Basic?

I still like Basic, never mind it's not the programming language flavor of the (week, month  year).

I guess a related question is which operating systems is it easiest--as in dead simple--to install the necessary bits so a person can program using your Basic?

Be well and Happy Holidays.

Ron K Jeffries
805-567-4670 mobile

--
You received this message because you are subscribed to the Google Groups "4tH-compiler" group.
To unsubscribe from this group and stop receiving emails from it, send an email to 4th-compiler...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Hans Bezemer

unread,
Dec 22, 2014, 11:11:00 AM12/22/14
to 4th-co...@googlegroups.com
On Monday 22 December 2014, Ron K. Jeffries wrote:
Hi Ron!

> You continue to amaze & delight your fan club. ;)
Thank you, I love that!

> A philosophical question, if you don't mind:
> Besides the always valid reason of "because I can" what do you see as the
> possible future of your ever-improving Basic?
Frankly, I really don't know. So far I've always updated it for one of the
three following reasons:
- In competition with other BASIC interpreters (e.g. TinyBasic2);
- Wondering if that could be added as well (new functionality, e.g. locals);
- Annoyance (worked, but seemed somehow quirky to me, e.g. non-numeric
labels).

And each and every time I thought: "it's perfect now" - like now. ;-)

The thing is, I don't wanna add kludges, I want to stay as close to the
architecture it already has. I always think I've reached the limit and then I
find it's possible anyway. Another idea I'm playing with is to add strings,
but I need to have a solid understanding where I'm going before I do it. But
as soon as I got it, it will probably just take days.

I usually don't make roadmaps. I'm playing with ideas, if I can't make 'em
work within the design objectives, I leave 'em be for the time being.
Sometimes they emerge again, I get the right ideas and they're there. That
goes for 4tH as well. Note the extension of today took about 25 lines - of
which half of 'em are just declarations (~60 bytecode instructions).

4tH examples: floating point, GCC "goto"s, peephole optimizer. uBasic
examples: IF..ELSE..ENDIF, non-numeric labels and FUNC() and friends (yes,
I've been playing with that one for some time too).

> I still like Basic, never mind it's not the programming language flavor of
> the (week, month year).
I don't know why I like it, but it was my first programming language and I
still got lots of (ancient) material lying around.

> I guess a related question is which operating systems is it easiest--as in
> dead simple--to install the necessary bits so a person can program using
> your Basic?
The easiest is to make it a native executable. I've done so for my girlfriend,
who now learns to program using uBasic. Making a native executable isn't too
hard, but if anyone would like to see a binary for their particular operating
system, lemme know. It takes a matter of minutes.

> Be well and Happy Holidays.
From the heart: same to you and yours!

Hans Bezemer
--
I have no Facebook account. Consequently, I have no friends and I don't like
anything. Deal with it.

Visit our website! http://thebeez.home.xs4all.nl/4tH/

*** Home of the 4tH compiler! ***

Ron K. Jeffries

unread,
Dec 22, 2014, 11:37:26 AM12/22/14
to 4th-co...@googlegroups.com
Native executable of uBasic sounds awesome. 
Pls. tell us which OS you have already made the standalone uBasic version work with? 

I use: 

Android 4.4 (a lot, on a large screen phone)
Windows 8.1 (some)
ChromeOS (I know, that ain't happening anytime soon)
Linux (very occasionally, and I feel GUILTY about that fact. Maybe... I'm a FRAUD?)

Ron K Jeffries



---
Ron K. Jeffries





Hans Bezemer

unread,
Dec 22, 2014, 12:12:50 PM12/22/14
to 4th-co...@googlegroups.com
On Monday 22 December 2014, Ron K. Jeffries wrote:
> Native executable of uBasic sounds awesome.
> Pls. tell us which OS you have already made the standalone uBasic version
> work with?
Android, RasPi, Ben Nano, Linux 32/64 bit, Win32, Android, DOS and Coherent.
It (most) probably runs on many other *nixes, but I don't have access to
these.

BTW, already using 4tH on Android?

The Beez

unread,
Dec 24, 2014, 5:22:26 AM12/24/14
to 4th-co...@googlegroups.com, han...@xs4all.nl


On Monday, December 22, 2014 6:12:50 PM UTC+1, Beestje wrote:
Ok, I've been running the new UBASIC a LOT to see if there are any bugs left, e.g. stack leaks, but it runs quite nice. I've uploaded the new syntax file for Kate and NOTONE.BAS just to show you how it cleans up many uBasic programs. In short, it makes the user stack fully transparent if you want to.

But now into the techniques used: how does it work?

Well, PARAM is easy: it gets the current LOCAL frame pointer, calls "LOCAL" and then sees how many extra local variables were added. Then it skips the current frame pointer and offloads the user stack until the previous frame pointer is overwritten:

  frame exec_local frame
  begin over over > while cell+ (pop) over ! repeat drop drop


It's that easy! You may complain it's not elegant enough, but it's rock solid because "LOCAL" and "POP()" take care of everything.

The extension of "GOSUB" and "PROC" - which is the same - is just as simple. It checks briefly whether it is followed by a "(" delimiter. If not, it's left to the interpreter for further resolution:

  get_token paren? putback if ['] exec_push exec_function then


If it is, "PUSH" is called as if it were a "function" (that is: surrounded by parentheses). A similar trick is done for "RETURN", although only one expression is accepted.

The real clever thing is "FUNC()", which turns every subroutine into a true function. The problem is that when it returns the whole thing must be finished. You can't let the interpreter loop resolving the rest. Since the basic interpreter is defined in the very end of the program and the functions have to be defined at the start, a DEFER is needed, since the semantics for FUNC() can be defined in the last few lines. FUNC() start off by calling GOSUB:

  exec_gosub gstack a@ >r

You may think it's a bit dirty, but:
  1. Since GOSUB does all the work, we can be assured that there is a valid value on the (BASIC) return stack;
  2. The alternative is to factor out the exec_gosub code, leave the last program position, do a call to the factored code from exec_gosub and drop the value. You decide what's dirtier..
The only way to encapsulate a complete execution within the FUNC() call is to start new interpreter. At one moment in time RETURN will pop the value from the (BASIC) return stack and then we'll know we've returned to the original position, i.e. we've completed the FUNC() call:

  begin interpret r@ prog @ = until

It's very hard to mess with that, although you may try (don't use GOTO), because a call to the routine that places the FUNC() call will always have to pass by the FUNC() call itself, starting a new interpreter. If you manage to do that, I'd like to know ;-)

Finally, if we've returned, we have to drop the value from the 4tH return stack and call POP() to get the value placed there by RETURN:

  r> drop (pop)

And that's it! We've turned a GOSUB call into a function and made the user stack manipulation truly transparent. If you make an error, the calls to the PUSH and POP() semantics will trap it and report it as if they were called directly.

I hope I haven't bored you too much with these internals. ;-)

Hans Bezemer


notone.bas
ubasic.xml

The Beez

unread,
Dec 24, 2014, 12:54:21 PM12/24/14
to 4th-co...@googlegroups.com, han...@xs4all.nl
On Wednesday, December 24, 2014 11:22:26 AM UTC+1, The Beez wrote:

My wife is not happy on Xmas night:

  Updated UBASIC.4tH http://sourceforge.net/p/forth-4th/code/871/

I'm gonna make her smile again ;-)
Signing off!

Hans Bezemer

Ron K. Jeffries

unread,
Dec 24, 2014, 2:35:29 PM12/24/14
to 4th-co...@googlegroups.com, han...@xs4all.nl
Genius. Pure, effing genius.

Steven Hawking would be proud. 
This has a certain black hole quality, no?
It radiates energy.
Has no mass.

Be well
Ron K Jeffries

---
Ron K. Jeffries





--
Reply all
Reply to author
Forward
0 new messages