Stretching the definition of BASIC even further

In the last post I pondered if it’s still BASIC without GOTO. I think it is, but then how far can you go with a language before it loses its identity? Or worse, before it starts to look like Visual Basic? 😱

I’ve abandoned variable (and function) suffixes having semantic meaning. It’s just name and left(name,3) instead of name$ and left$(name$,3).

I’ve abandoned GOTO and GOSUB. And of course, line numbers.

But I’ve kept LET (mandatory on some BASICs and optional on most).
I’ve kept DIM but I’m using this only for arrays as it was originally intended.
The standard numeric and text functions are all there.
There’s FOR NEXT and WHILE WEND.

I’ve resisted the urge (so far) to use lambdas, but as we’ll see, we do have closures, and I suspect function references and lambdas will make an appearance later on.

What else?

Scoping

Variables are scoped to the block in which they are defined.

let x=9 ' int
sub foo()
  let x=0.1 ' float
  if x>0.0 
    let x="fish" ' string
  endif
  def bar(x as bool) as bool = not x
end
for n=1 to 5:next
print n 
' error, n was scoped to for loop

Here there are four different x variables in different scopes, and the variable n is scoped to the for loop and so not visible outside.

If one were to be typing in old BASIC programs into SoapyBASIC this will certainly catch people out. A price worth paying for stronger semantic structure, I think.

Dynamic Arrays

In SoapyBASIC, you can create an array in the usual way (apart from declaring type):

dim q[3] of int
q[0]=10
q[1]=12
q[2]=23
q[3]=77 ' error, out of bounds

But you can also create arrays as literals and they can be explicitly grown:

let r=[3,5,17]
let t=q+r '[10,12,23,3,5,16]

They can be spread and coalesced through function calls.

let p=[3,7,11]
func sum(ns... as int) as int
  let t=0
  for n in ns
    t=t+n
  next
  return n
endfunc
print sum(...p) ' 21
print sum(4,5,6) ' 15

Arrays can be nested and returned from functions:

func mkarr(n as int, v as float) as array of float
  dim a[n] of float
  for x=0 to n-1:a[x]=v:next
  return a
endfunc
let vs=mkarr(30,2.5)

let vss=[vs,vs]
print vss is array of array of float 'true

I still there’s enough BASICness in the above, but also some genuinely useful structure and semantics.

Strings

As my SoapyBASIC interpreter (and now in progress, compiler) is written in Swift, my BASIC strings are fully Unicode.

Unlike Rust that just uses utf8 and offers other models, SoapyBASIC strings are proper strings of characters, and these represent (usually) a grapheme cluster.

> print len("café") '4
> let acute=chr(&h301)
> print len("cafe"+acute) '4
> print len("😀") '1
> print utf8("😀") '[240, 159, 152, 128]
> print scalars("😀") '[128512]
> print from_utf8([240, 159, 152, 128]) '😀

So for most cases, BASIC programs will just work with strings, even if the program encounters unicode. It can offer conversions between code points, scalars and utf8 sequences.

> let s="cafe"+chr(&h301)
> print s len(s) scalars(s)
café 4 [99, 97, 102, 101, 769]
> let t=formc(s)
> print t len(t) scalars(t)
café 4 [99, 97, 102, 233]

Summary

I think I’ve struck a good balance between BASICness and modern utility.

As I’m now writing a proper compiler for SoapyBASIC (it’s an interpreter at the moment), I may choose some different paths here, as I’m quite likely to have the runtime for my compiler NOT written in Swift (probably just C), so the convenience of Swift won’t be there.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *