Beginning Student Language (BSL) is a programming language in DrRacket. This repository provides some useful BSL-Tools, such as

  • Abstract Syntax Tree
  • Stepper (Auswertungskontext)
  • BSL "Try out" box (Generator)

and semantical support for other grammars.

The module is distributed as a single JavaScript file and an optional additional Racket file for use with Scribble.

The documentation and demos are available at https://se-tuebingen.github.io/bsl-tools/.

You can download them from Releases, the current test build shows the "Development Build" and corresponds to the state of the main branch, you can download semantic versioned editions.

BSL Abstract Syntax Tree

Here we provide the grammar of the BSL Core Language from the Info 1 script, which was our motivation to develop these tools.

<program>     ::=    <def-or-expr>*
<def-or-expr> ::=    <definition> | <e>
<definition>  ::=    ( define ( <name> <name>+ ) <e> )
               |     ( define <name> <e> )
               |     ( define-struct <name> ( <name>* ) )
<e>           ::=    ( <name> <e>*)
               |     ( cond {[<e>,<e>]}+)
               |     <name>
               |     <v>

For reference see also the Racket webpage for BSL and the Info 1 script at 8.3

Building the project

For building the project, you need to have CMake or a similar tool that can handle the Makefile installed. For Linux and MacOS, this should already be the case.

You also need to have NodeJS and NPM installed. On Ubuntu, it is recommended to install it via NodeSource.

You need to run npm install in a newly cloned project and anytime dependencies might have changed.

Then, you can run:

  • make build to compile and bundle everything under src - grammar, TypeScript sources and assets - into dist/bsl_tools.js. (Note that the command does not terminate, the TypeScript compiler keeps watching the source for changes.)
  • make test to compile the Scribble Demo (requires Racket as described below). This also copies the current version of the JavaScript file to the HTML Test files directory (which is not required for them to load the latest version locally, but for distribution e.g. via Github workflow to test on mobile devices).
  • make doc to compile the documentation under docs/src and docs/demo to docs/book (result is opened in your browser automatically). For the scribble demo to be included, you need to have run make test first.

Dependencies

Install the latest TypeScript version via npm. Also see https://www.typescriptlang.org/download.

We are using esbuild to compile TypeScript and bundle all resources into one single JavaScript file.

For generating a BSL parser, we are using the ts-pegjs package, which builds upon pegjs. This package installs a node script which compiles the grammar found in src/grammar/bsl.pegjs to a TypeScript parser module. The best way to test and edit the grammar is the pegjs online version, since it has syntax highlighting and live testing.

In order to generate a NodeJS-independent binary (effectively, a binary with NodeJS bundled, so not exactly lightweight) for the fallback build out of ts-pegjs, we are using the pkg package.

If you want to contribute to the scribble part of bsl-tools you need to install Racket first. See https://download.racket-lang.org/

You also need to install dependencies for Scribble, the Racket Documentation Tool, preferably with the racket package manager raco. On Linux distributions you can install Scribble with raco pkg install --deps search-auto scribble-math.

Fallback build

In the branch fallback_build, there are binaries of tspegjs, esbuild and mdbook for Mac, Windows and Linux 64bit systems as well as Makefile entries for using (fallback_build and fallback_doc) and updating them. This is to future-proof the toolchain, if updates break something or a tool is no longer supported.

If the regular build does still work, it might be a good idea to checkout the branch and run make update_fallback_build to keep the binaries up-to-date from time to time. We split this to a separate branch, however, to make the repo a little bit more lightweight and spare you unnecessary megabytes of downloads.

Testing

Running make test copies the generated JavaScript file and the latest version of the Scribble Plugin to the test folders and renders the Scribble test.

In order to be able to render the Scribble test pages, you need to have Racket installed.

The HTML test pages load the JavaScript plugin from the dist folder if opened locally (served via file://), so you do not need to run make test to update it - leaving the make build command running works fine.

Publishing a version

Everytime a tag starting with v is published, a github workflow similar to the one running when something is pushed on main will kick off, compile and test the code. If the tag has the form vX.X.X (3 numbers), the module will be released under this name, otherwise, the workflow will fail.

AST View

The Abstract Syntax Tree View shows the Abstract Syntax Tree of a given expression in the BSL grammar (or another given grammar in JSON).

Using AST View with HTML

Including bsl_tools.js

To use the AST-View, you need to add the script anywhere in your document as follows:

<!-- import the bsl_tools script somewhere on the page, ideally in the head -->
<script src="bsl_tools.js"></script>

Note that if you add one or more of the following tools to your page, the script will add stylesheets to the <head> of your document which set styles for the .bsl-tools-stepper and .bsl-tools-tree classes.

Planting the <bsltree>

The collapsible AST view can be used with BSL code, which will be parsed for you, or you can provide your own JSON structure.

The interface is as follows:

<bsltree> (valid bsl syntax) </bsltree>

The collapsible AST view also provides an optional "quiz" mode that starts the tree collapsed and expands if you select the correct production and mark all subexpressions ('holes').

<!-- if you want to display it in quiz mode, do -->
<bsltree quiz="true"> (valid bsl syntax) </bsltree>

<!-- for displaying user text in german, do -->
<bsltree quiz="true" lang="de"> (valid bsl syntax) </bsltree>

<!-- displaying code trees for stuff that is not BSL -->
<jsontree quiz="true" lang="de">
  <!-- same options as for bsltree -->
  { "production": "Subtraction", "code": "(|2| - |3|)", "holes": [ {
  "production": "Number", "code": "2"}, { "production": "Number", "code": "3"} ]
  }
</jsontree>

Anything inside the <bsltree> tag is parsed according to the BSL Core Language as documented below.

< and > need to be replaced with &lt; and &gt; in order to not break your HTML!

If you want to use <jsontree>, you need to provide valid JSON - so use double quotes (")! "code" always needs to be a string, mark holes (code of children) by putting |s around it. If you want to, you can provide your own grammar in the "grammar" field of the root node, like this:

...
"grammar": {
  "production name": ["rule 1", "rule 2"],
  ...,
  "terminal symbol": [],
  ...
}
...

Note that the object keys need to match exactly the productions you use in your nodes. The Rules are shown as tooltips on hover, and the keys are used as options in quiz mode. If no grammar is specified, quiz mode options are inferred from all occurring productions.

The lang attribute is only applicable for Quiz mode, since the regular tree does not display any text in natural language. Currently implemented codes are en(English, default) and de(German).

Custom Styling

If, for example, your production labels are significantly larger than the text in your nodes, you can give the nodes (represented by <span> elements) greater horizontal spacing with

jsontree .bsl-tools-tree span {
  margin-left: 5em;
  margin-right: 5em;
}

Note that you need to scope your styles to .bsl-tools-tree, or they are less specific than those in the general stylesheet and will be overridden. Also note that inline CSS is not scoped and will apply to all your jsontree elements (or whatever you specify).

If you want to change colours, you can also provide css that is more specific than just .bsl-tools-tree and override the CSS variables set at the top of src/ressources/tree.css.

Using AST View with Scribble

Including the Files

To use the provided scribble module, you need to import it in your document:

@(require "bsl_tools.rkt")

The scribble module does only very little input sanitation and mostly just adds the JavaScript-Module to your rendered HTML as well as wrapping the input in the correct custom HTML tags. Both files (bsl_tools.rkt AND bsl_tools.js) need to be in the same folder as well as your scribble file so that everything works.

Scribbling the Trees

For the collapsible AST Production Trees, the interface in Scribble is as follows:

  • For BSL Expressions, we use Rackets Syntax Objects, which have a similar structure as S-Expressions
  • For other Expressions, you need to provide a JSON object with the correct structure, which will not be checked at compile-time!
@bsltree[
  #:quiz #t   @; optional keyword argument, default is #f
  #:lang "de" @; optional keyword argument, default is "en"
  #'((valid bsl syntax))
]

@jsontree[
  #:quiz #t   @; optional keyword argument, default is #f
  #:lang "de" @; optional keyword argument, default is "en"
]{
  {
    "production": "Subtraction",
    "code": "(|2| - |3|)",
    "holes": [
      { "production": "Number", "code": "2"},
      { "production": "Number", "code": "3"}
    ]
  }
}

It is necessary to wrap the BSL-Syntax with a #'(), especially when there are multiple <def-or-expr>, however literal values, such as 2 don't need to be wrapped. The Scribble module parses the BSL-Syntax to a string in a <bsltree> and adds the javascript module as dependency.

The JSON structure for non-BSL-Syntax needs to contain

  • a "production" name (string)
  • some "code" (string) optionally with holes fenced by |
  • optionally under "holes" a list of children with the same structure
  • optionally the root node can contain a custom "grammar" as described in the html API above

If you set the optional keyword argument #:quiz to #t, it will add quiz="true" which displays the tree in quiz mode.

Similarly, the #:lang keyword argument can be used to set the language code. This is only applicable for Quiz mode, since the regular tree does not display any text in natural language. Currently implemented codes are en(English, default) and de(German).

For the @jsontree-helper, there also is the #:extrastyle option, whose string argument will be added inside a HTML <style> tag above the HTML that is processed by the Javascript part. This way, you can override some styles, but not set CSS variables (-- will be converted to &ndash;, sadly). If, for example, your production labels are significantly larger than the text in your nodes, you can give the nodes (represented by <span> elements) greater horizontal spacing with

#:extrastyle "jsontree .bsl-tools-tree span { margin-left: 2em; margin-right: 2em;}"

Note that you need to scope your styles to .bsl-tools-tree, or they are less specific than those in the general stylesheet and will be overridden. Also note that inline CSS is not scoped and will apply to all your jsontree elements (or whatever you specify).

Example AST View

Here you can see a few examples of valid Abstract Syntax Trees in BSL Core Language. (cond [e1 #t] [else (asBool e2)])

AST as a quiz

(define (f x y) (+ x y))

AST in German

(define-struct name (firstName lastName))

Build your own grammar!

Given is the following Grammar:

    "grammar": {
      "<FavouriteNumber>": ["<RoundedNumber>", "<Mystery>"],
      "<RoundedNumber>": ["0", "3", "6", "8", "9"],
      "<Mystery>": ["<Pair>+", "7"],
      "<Pair>": ["<RoundedNumber><RoundedNumber>"],
    }

We can also make quizzes based on this custom Grammar. For example, why is 68087 a FavouriteNumber?

{ "grammar": { "FavouriteNumber": ["RoundedNumber", "Mystery"], "RoundedNumber": ["0", "3", "6", "8", "9"], "Mystery": ["Pair+7"], "Pair": ["RoundedNumberRoundedNumber"] }, "production": "FavouriteNumber", "code": "|68087|", "holes": [{ "production": "Mystery", "code": "|6808|7", "holes": [{ "production": "Pair+", "code": "|68||08|", "holes": [{ "production": "Pair", "code": "|6||8|", "holes": [{ "production": "RoundedNumber", "code": "6" }, { "production": "RoundedNumber", "code": "8" } ] }, { "production": "Pair", "code": "|0||8|", "holes": [{ "production": "RoundedNumber", "code": "0" }, { "production": "RoundedNumber", "code": "8" } ] } ] }] }] }

Small Step Interpreter (Stepper)

The Small Step Interpreter (Stepper) shows each step of BSL programs.

The stepper accepts only syntactic valid BSL programs. It precomputes the evaluation steps and allows users to step through the evaluation of each expression until the program is finished.

Using the Stepper with HTML

Including bsl_tools.js

To use the AST-View, you need to add the script anywhere in your document as follows:

<!-- import the bsl_tools script somewhere on the page, ideally in the head -->
<script src="bsl_tools.js"></script>

Note that if you add one or more of the following tools to your page, the script will add stylesheets to the <head> of your document which set styles for the .bsl-tools-stepper and .bsl-tools-tree classes.

Reducing BSL programs with <stepper>

The interface is as follows:

<stepper> (valid bsl syntax) </stepper>

<!-- for displaying user text in german, do -->
<stepper lang="de"> (valid bsl syntax) </stepper>

< and > need to be replaced with &lt; and &gt; in order to not break your HTML!

Font sizes and styles are deliberately left unset wherever possible in order to just conform to the styling of the rest of the document.

For the code and explanation formats, however, setting the font-family attributes is a must. The current stylesheets has chosen some specific fonts, if you do not agree with them or want to choose your own font, you simply need to override the CSS variable:

.bsl-tools-stepper {
  --font-family-monospace: monospace; /* to use the system default */
  --font-family-rule-description: "My fancy font", serif; /* to use your own choice */
}

Place this somewhere where it overrides the default stylesheet that is added to the document head, e.g. in a style tag above the first stepper. Other CSS variables that can be set can be found at the head of src/ressources/small-interpreter.css.

Using the Stepper with Scribble

Including the Files

To use the provided scribble module, you need to import it in your document:

@(require "bsl_tools.rkt")

The scribble module does only very little input sanitation and mostly just adds the JavaScript-Module to your rendered HTML as well as wrapping the input in the correct custom HTML tags. Both files (bsl_tools.rkt AND bsl_tools.js) need to be in the same folder as well as your scribble file so that everything works.

Similar to the AST view functionality, programs are entered as Racket Syntax Objects.

@stepper[
  #'((* (+ 1 2 (- 3 9 12) (/ 200 4 5)) (/ 1 2 3) 2)
  (cond [(>= 5 5) "isThree"]
  [#false 3]
  [(or #true #false) (* 2 3 4)]))
]

For German Language support add #:lang "de" as additional parameter:

@stepper[ #:lang "de"
#'((_ (+ 1 2 (- 3 9 12) (/ 200 4 5)) (/ 1 2 3) 2)
(cond [(>= 5 5) "isThree"]
[#false 3]
[(or #true #false) (_ 2 3 4)]))
]

Stepper Example

(* (+ 1 2 (- 3 9 12) (/ 200 4 5)) (/ 1 2 3) 2) (cond [(>= 5 5) "isThree"] [#false 3] [(or #true #false) (* 2 3 4)]) (cond [(cond [(and #false #true) #false] [#false 3] [(or #true #false) #false]) "isThree"] [#false 3] [(< 2 3) (* 2 3 4)]) (define x (+ 40 2)) (define y "hallo") (define-struct p (x y)) (define p1 (make-p 1 2)) (p? p1) (p-y p1) (> 2 4)

Generator

Adding a simple bsltoolsgenerator tag in your HTML, like so:

<!-- import the bsl_tools script somewhere on the page, ideally in the head -->
<script src="bsl_tools.js"></script>

<bsltoolsgenerator></bsltoolsgenerator>

gives you the textarea and buttons below, which you can use to generate your your own Stepper and AST Views, if you give valid BSL programs! Unfortunately there is no support for Scribble yet.

Scribble Demo