An implementation of Neil D. Jones' While language.
An implementation of Neil D. Jones' While language. Developed in collaboration with Dr. Bernhard Reus (University of Sussex, UK) for use in the Limits of Computing module.
HWhile
HWhile is an interpreter for a simple While language originally used by Neil Jones in his book “Computability and Complexity". The While language is a simple imperative programming language with while-loops, assignment and conditionals. It has a single data type: binary trees. The HWhile interpreter is written in Haskell.
This current implementation also includes syntactic sugar such as switch statements, macros, list notation, equality, and natural number literals. It therefore supports (almost fully) the version of While used in Bernhard Reus’ textbook Limits of Computation - From a Programming Perspective and has been developed in co-operation with Bernhard to support students on the corresponding module at Sussex University. This version also allows one to translate while programs into programs as data. For this to work, all the syntax sugar (extensions) of a While program has to be removed again.
More about the syntax and the semantics (and usage) of the While language can be found in Bernhard’s textbook (Chapter 3-5), and we also include a summary below.
Instructions
Installing Prerequisites
All the tools required to compile and run HWhile are included in the Haskell Platform. Make sure you install the full version as opposed to the core version.
Note that you may need to add the Haskell Platform's binaries to your system's path variable. If you're on windows this should happen automatically. Otherwise some configuration may be required.
Installing HWhile
Once the Haskell Platform is installed and configured correctly, you can install HWhile by running:
stack install hwhile
This will download HWhile and its depenencies (if necessary) and compile and install them.
Invocation
If installed correctly, HWhile can be run with the command:
hwhile <FLAG> <FILE> <EXPR>
For example:
hwhile -i examples/count.while "[1, 2, 3]"
This example takes a list of numbers as its argument and outputs their sum, so you should see 6
as the output.
Syntax
The grammar below gives exactly the concrete syntax of this implementation:
PROG ::= ID read ID BLOCK write ID
BLOCK ::= { CMDS }
| {}
CMDS ::= CMD ; CMD
| CMD
CMD ::= ID := EXP // Assignment
| while EXP BLOCK // While loops
| if EXP BLOCK else BLOCK // If-then-else statements
| if EXP BLOCK // If-then statements
| ID := <ID> EXP // Macro calls
| switch EXP { CASES // Switch statements
CASES ::= case EXP : CMDS CASES
| default : CMDS }
| }
EXP ::= LIT
| cons EXP EXP
| hd EXP
| tl EXP
| (EXP)
| EXP = EXP
| ID
| []
| [ EXP LIST
LIST ::= , EXP LIST
| ]
LIT ::= nil
| true
| false
| <LIT.LIT>
| NAT
| @asgn
| @:=
| @doAsgn
| @while
| @doWhile
| @if
| @doIf
| @var
| @quote
| @hd
| @doHd
| @tl
| @doTl
| @cons
| @doCons
NAT ::= 0|[1-9][0-9]+
ID ::= [a-zA-Z_'][a-zA-Z0-9_']*
Command line input must conform to the following grammar:
INP ::= nil
| true
| false
| <LIT.LIT>
| NAT
| []
| [ INP INPLST
| @asgn
| @:=
| @doAsgn
| @while
| @doWhile
| @if
| @doIf
| @var
| @quote
| @hd
| @doHd
| @tl
| @doTl
| @cons
| @doCons
INPLST ::= , INP INPLST
| ]
Semantics
The semantics of HWhile programs are defined by functions. These functions take a program construct - an expression, block, command or program, and a store - a function of type X -> T
that maps variable names to trees.
We first define two functions over stores - addition, which adds a new binding, and lookup, which retrieves the tree bound to the given name.
σ + (x -> T) = σ, x -> T if x ∉ σ
| σ', x -> T, σ'' if σ = σ', x -> U, σ''
σ(x) = T if σ = σ', x -> T, σ''
| nil if x ∉ σ
Semantics for expressions are defined as follows:
[e] : EXP -> (X -> T) -> T
[x] σ = σ(x)
[hd e] σ = nil if [e] σ = nil
| H if [e] σ = <H.T>
[tl e] σ = nil if [e] σ = nil
| T if [e] σ = <H.T>
[e = e'] σ = nil if [e] σ /= [e'] σ
| T if [e] σ = [e'] σ where T /= nil
[cons e e'] σ = <H.T> where H = [e] σ, T = [e'] σ
For blocks:
[b] : BLOCK -> (X -> T) -> (X -> T)
[{}] σ = σ
[{c; cs}] σ = [{cs}] ([c] σ)
For commands, we define semantics only for pure commands (assignment, while loops and if-then-else statements). The semantics for switch statements and macro calls are given by translation to pure commands. The semantics for pure commands are given as follows:
[c] : CMD -> (X -> T) -> (X -> T)
[x := e] σ = σ + (x -> [e] σ)
[while e b] σ = σ if [e] σ = nil
| [while e b] ([b] σ) if [e] σ /= nil
[if e b else b'] σ = [b] σ if [e] σ /= nil
| [b'] σ if [e] σ = nil
The translations for if-then statements, switch statements and macros are defined as follows:
if e b = if e b else {}
switch e {} = {}
switch e { default : cs } = { cs }
switch e1 { case e2 : cs cases } =
{ if e1 = e2 { cs } else switch e1 { cases } }
x := <macro> e = z := e; disjoin(cmds, z, y); x := y
where macro.while = macro read z { cmds } write y
The function disjoin
renames the variable names in cmds
such that there are no names in common with the program in which the macro call occurs, except for the read-variable z
and the write-variable y
.
Finally, the semantics for programs are defined as follows:
[p] : PROG -> T -> T
[n read x b write y] T = ([b] (x -> T))(y)