Phillip Trelford's Array

POKE 36879,255

Extending Small Basic with Function Procedures

Microsoft Small Basic is a minimal implementation of the BASIC language aimed at beginners, with only 14 keywords.

A few years back I used Small Basic as an early introduction to programming for my 2 young children (at the time 9 and 5). Small Basic’s library has a nice simple API for drawing shapes and moving a turtle around the screen which the kids loved. However I found the lack of function arguments and return values was quite limiting from a teaching perspective. After creating shapes we wanted to refactor the code so that the drawing procedures could be parameterized, which can only be achieved with global state. Not wanting to get my kids in to bad habits early we swiftly moved on to F#.

Coincidentally Small Basic’s library is a .Net assembly that can be easily consumed from C#, F# and VB.Net, which I’ve used once while introducing C# programming to adult beginners.

Inspired by Small Basic’s simplicity, I’ve also built an open source library along similar lines called SmallSharp which I’ve used on occasion to introduce programming in F#. I feel the ability to start drawing shapes on the screen with just a few commands gives a real sense of excitement and achievement. The Processing language is another good option in this space. In contrast large frameworks like WinForms and WPF, although highly customizable, typically require a lot of work up front before you see any results.

In the previous three articles I’ve described steps to building a Small Basic compiler. First creating an abstract syntax tree (AST) with F# discriminated unions, then parsing with the FParsec parser combinator library and finally transforming to CIL with reflection emit.

In this article I’ll give some details on how I’ve extended the compiler with function procedures, bringing the functionality of Small Basic closer to that of VBScript.

The source code including the function procedure extension is available on BitBucket.

Extending the AST

First off the AST must be extended to support function declarations:

    | Sub of identifier * string list
    | EndSub
    // Language extensions
    | Function of identifier * string list
    | EndFunction

Next a way is needed to call functions:

and invoke =
    | Method of string * string * expr list
    | PropertyGet of string * string
    | Call of string * expr list // Language extension

Extending the Parser

Now the parser needs to recognise the new syntax:

let pparams = 
    between (str_ws "(") (str_ws ")") (sepBy pidentifier_ws (str_ws ","))
let pmethod = 
    pidentifier_ws .>>. opt pparams
    |>> (fun (name,ps) -> name, match ps with Some ps -> ps | None -> [])
let pfunction = 
    str_ws1 "Function" >>. pmethod |>> (fun (name,ps) -> Function(name,ps))
let pendfunction = 
    str_ws "EndFunction" |>> (fun _ -> EndFunction)

The Function keyword expects a method declaration which is composed of an identifier and optional parameters between parentheses.

Calls in expressions are recognized as a single identifier with a list of arguments:

let pcall = pidentifier_ws .>>. ptuple 
            |>> (fun (name,args) -> Call(name, args))


Code Generation

The code generator now needs to generate methods for both Sub and Function statements, with void and Primitive return values respectively. The generated methods may also have named arguments, passed by value. When an identifier is referenced in a statement the generator checks the method arguments in precedence to global fields. Finally return values are taken from the field with the same name as the method, as is the convention with the Visual Basic series of languages.

Sample

Here’s FizzBuzz in Small Basic utilizing the new extension:

' Returns Modulus
Function Modulus(Dividend,Divisor)
  Modulus = Dividend
  While Modulus >= Divisor
    Modulus = Modulus - Divisor
  EndWhile
EndFunction

For A = 1 To 100 ' Iterate from 1 to 100
  Mod3 = Modulus(A,3) ' A % 3
  Mod5 = Modulus(A,5) ' A % 5
  If Mod3 = 0 And Mod5 = 0 Then
    TextWindow.WriteLine("FizzBuzz")
  ElseIf Mod3 = 0 Then
    TextWindow.WriteLine("Fizz")
  ElseIf Mod5 = 0 Then
    TextWindow.WriteLine("Buzz")
  Else
    TextWindow.WriteLine(A)
  EndIf
EndFor

Conclusions

Extending the language touched small parts of the AST, parser and code generation steps. The whole process took only a couple of hours.

With the simple addition of function procedures with arguments and return values, Small Basic starts to approach the functionality of VBScript, and feel more like a grown up language for scripting.

It’s also starting to feel like the Small Basic AST and parser could be easily extended to support any of the Visual Basic family of languages, from VBScript to VBA to VB.Net.

Future

Just as Small Basic currently has an export to VB.Net option, another interesting future direction might be to transform Small Basic programs to JavaScript allowing truly cross platform deployment.

Pingbacks and trackbacks (1)+

Comments are closed