Download a zip file containing all of the source files that you need for this assignment.

You will need to edit Main.hs and Tests.hs, the plain-Haskell version of the assignment files.

Remember that, as you complete the assignment, you should be testing. All of the test cases and properties are in Tests.lhs. You should edit this file to add your own testing code. The test cases will also help specify parts of the problems.

Read over the .imp files for this assignment, which are sample files for a simple imperative programming language (the WHILE language). Take a look at these files to get an idea of the concrete syntax of the WHILE language.

Note, although the concrete syntax looks like C or Java, it is not exactly the same as those languages.

This homework assignment draws on many libraries. So that you can tell where to look for the definitions, we will either import them explicitly (as in Applicative/Monad below) or qualify the imports (as in Data.Map and Text.PrettyPrint). If you want to use more functions from Applicative/Monad, you may add them to the explicit import list.

We will represent the store i.e. the machine's memory, as an associative map from Variable to Value:

>typeStore=MapVariableValue

Note: we don't have exceptions (yet!), so if a variable is not found (eg because it is not initialized) simply return the value 0. In the future, we will add this as a case where exceptions are thrown (the other case being type errors.)

Test Programs

>------------------------------------------------------------------------->-- Here are some test programs. You can ignore the 80-column limit for this part>-- of the file.

Statement Evaluator

Next, write a function

> evalS ::Statement->StateStore ()

that takes as input a statement and returns a world-transformer that returns a unit. Here, the world-transformer should in fact update the input store appropriately with the assignments executed in the course of evaluating the Statement.

such that execS stmt store returns the new Store that results from evaluating the command block from the world store. Hint: You may want to use the following library function from the State module.

execState ::State s a -> s -> s

When you are done with the above, the following function will "run" a block of statements starting with the empty store (where no variable is initialized). Running the program should print the value of all variables at the end of execution.

A pretty printer is a function that converts an abstract syntax tree into a readable representation of the concrete syntax. Your job on this problem is to use the HughesPJ library to develop a pretty printer for WHILE.

For background reading, Ch. 5 of Read World Haskell goes through the design of a library called, "Prettify", which is a simplified version of the HughesPJ library. Note that most of the definitions in the library (such as char, text, etc.) are imported qualified as PP.char so that they do not conflict with the other parts of this assignment.

The HughesPJ library provides the following to assist in the development of pretty printers:

An abstract type Doc of "pretty documents" that know how to lay themselves out prettily. We can use this type to define a class of of types that support pretty printing---those that define a function mapping any value of that type to a suitable Doc.

>classPP a where> pp :: a ->Doc

Primitive documents and operations for constructing Docs from primitive types, such as characters and string.

Combinators for combining Docs in various ways, providing constraints on the textual layout. For example, some are listed below. (See the library documentation for many more.)

-- Beside. Combines two documents horizontally with no space between.
(<>) :: Doc -> Doc -> Doc
-- Beside, separated by space, unless one of the arguments is `empty`.
(<+>) :: Doc -> Doc -> Doc
-- Nest (or indent) a document by a given number of positions
-- (which may also be negative).
nest :: Int -> Doc -> Doc
-- Above. Combines two documents vertically (with overlap if
-- possible: if the last line of the first argument stops at
-- least one position before the first line of the second begins,
-- these two lines are overlapped).
($$) :: Doc -> Doc -> Doc
-- List version of $$.
vcat :: [Doc] -> Doc
-- wrap document in (..)
parens :: Doc -> Doc

Operations for rendering, or converting a Doc to text at the top level. The rendering functions are parameterized over display options, such as the maximum line length, so that they can figure out how to best display the text. For example, after you finish this problem, you should be able to produce the output above by calling the following two functions on wFact.

Your job is to fill in the definitions of pp for Values, Expressions, Statements and Blocks. Note that you should let the pretty printer make decisions about line breaks (i.e. oneLine vs. indented above). To do this you should never insert explicit newline and space characters---use the combinators above instead.

Your code for your pretty printer should pass the tests in Tests.hs for displaying values, expressions, and statement blocks. This concrete syntax resembles C or Java, but there are some simplifications! Take a look at the testcases to figure out how this abstract syntax should be displayed.

A Stepper for WHILE

Now that we have a way to display programs nicely, we can implement a "debugger" that we can use to step through the evaluation of imperative programs.

The first part of this definition is a function that will partially evaluate a block of statements. The step function (that you will write) below should do "one-step" of evaluation.

> step ::Block->StateStoreBlock> step _ = undefined

Note that this step function should always terminate, even if the input program does not. For examples of how step should operate, see the Test file.

Iterating this step function until we do not have any more statements to execute, should produce an alternative interpreter for the language. The execState function below should behave exactly the same as exec above.

>-- | Is this block completely evaluated?> final ::Block->Bool> final (Block []) =True> final _ =False

>-- | Evaluate this block for a specified number of steps> boundedStep ::Int->Block->StateStoreBlock> boundedStep = undefined

Finally, we can use the step function in a "stepper", an interactive program that lets us observe the computation piece-by-piece.

For example, here's an interaction with the factorial function.... Each line beginning with imp> is a prompt for the tool, allowing the user to type commands such as n (step to next statement), v x examine the value of variable x, or b (step backwards). Lines beginning with --> show the current step of the computation (before it has been executed).

A Parser for WHILE

The dual problem to pretty printing is parsing. For this part of the assignment, you will practice with the parser combinators that we discussed in class.

This part of the assignment uses the definition of the "Parser" type from Parser.hs. This module is augmented by the combinators from ParserCombinators.hs. You should read over these modules before continuing.

Again, the concrete syntax of the language resembles C or Java, but is a variant of those languages. Your parser does not have to be able to parse all of C; only the test cases that we provide. For example, C allows "if" statements to omit the "else" branch; but WHILE does not. Furthermore, "if" statements in C may omit braces, but they are always required here.

Parsing Constants

First, we will write parsers for the Value type

> valueP ::P.ParserValue> valueP = intP <|> boolP

To do so, fill in the implementation of

> intP ::P.ParserValue> intP = error "TBD"

Next, define a parser that will accept a particular string s as a given value x

> constP ::String-> a ->P.Parser a
> constP _ _ = error "TBD"

and use the above to define a parser for boolean values where "true" and "false" should be parsed appropriately.

> boolP ::P.ParserValue> boolP = error "TBD"

Continue to use the above to parse the binary operators

> opP ::P.ParserBop> opP = error "TBD"

Parsing Expressions

Next, the following is a parser for variables, where each variable is one-or-more lowercase letters.

> varP ::P.ParserVariable> varP = some P.lower

Now define a parser combinator which takes a parser, runs it, then skips over any whitespace characters occurring afterwards

> wsP ::P.Parser a ->P.Parser a
> wsP p = error "TBD"

Use the above to write a parser for Expression values

> exprP ::P.ParserExpression> exprP = error "TBD"

Tests.hs contains some tests for your expression parser. Of course, you will need to add some test cases of your own parser to that file. In particular, be sure to make sure that your parser succeeds even when there is white space within and after an expression.

Parsing Statements

Next, use the expression parsers to a statement parser

> statementP ::P.ParserStatement> statementP = error "TBD"

and one for the sequence of statements at the toplevel. (These should not be surrounded by braces.)