Lab 3: Parsing Party - Simple Infix Calculator

Posted on October 25, 2016

Announcements

Due to the midterm, this week’s lab will be due by 11:59:59pm Saturday, October 29.

Building a Simple Haskell Calculator

Later in the course you will consider monadic parsing but in this lab we will focus on the simple parsing problem of constructing a calculator. The goal is to gain some experience with handling strings and input.

The calculator should work as follows:

> 1 + 1
2
> 2 * 6
12
> (1 + 3) * (5 + 1)
24
> (1 + 02 + 3 * 2 + 6)
15
> (1 + (2 + ((4 + 2) * 6)))
39
> 5 +-6    -- This should be parsed as 5 + (-6)
-1

where user-input is indicated by a prepended >.

In this lab you will:

Skeleton Program

import System.IO


main :: IO ()
main = do
     putStr "> "
     hFlush stdout
     line <- getLine
     putStrLn line
     main

This is the basis for the calculator program. Call this file Main.hs.

In order to import your code for testing, you will need to create a separate Lab3.hs file which you import into the main skeleton above and into the test file. Most of your code will go into this Lab3.hs.

ArithExp

Define the ArithExp Data Type. Recursion is the name of the game.

This data type should represent:

The abstract ArithExp form for (2+3)*(3+2) would be

(Mult (Plus (Number 2) (Number 3)) (Plus (Number 3) (Number 2)))

Evaluating an ArithExp

Again, use recursion. eval should be a function that takes ArithExp and outputs a number. This is the first thing to get working. For division, use the quot function. Check that your eval works by ensuring that you get the right outputs for:

(Number (-3))
(Mult (Plus (Number 2) (Number (-6))) (Plus (Number 3) (Number 2)))
(Div (Number 13) (Number 6))
(Plus (Mult (Number 2) (Number 3)) (Div (Number 13) (Number (-6))))

and some other expression. Generate your own simple ones and make sure that things work. Put those in your sample runs file Tests.hs. For the test file, use the same format as last lab (e.g. see Submit below).

Parsing a String to ArithExp

The input will be *, -, +, [0-9], (, ), and /. Also, any - will always precede a number [0-9]. You do not have to handle bad input.

Here’s everything you’ll have to do:

Put tokenize and parse tests in Tests.hs. Once you are convinced you can tokenize and parse all simple expressions (and more complicated ones), ensure that eval can handle all of the expressions.

Work incrementally. Begin by tokenizing simple expressions:

1+2
1 + 20
1 * -2

Then get order of operations working correctly:

1 + 2 * 3 + 4
1 * 2 + 3 + 4

Then parse parentheses enclosing an entire expression:

(1+2)
 (1 + 2 )
(1 * 2 + 3)

Finally, work out how to recursively handle nested parentheses:

(-10 + 2) * 5
5 * (-10 + (2 + 4) * 3)
5 * (-10 + (2 + 4) * 3) * (3 + 2)

Handling parenthetical expressions is probably the trickiest part of the lab. You may choose to do it as part of tokenize or as part of parse.

Debugging

Debug.Trace will help you debug. Start with traceShowId.

Submit

The git submission system is almost ready, but this week you’ll be emailing your three files to brianhempel@uchicago.edu again.

Make a lab3 folder with Main.hs, Lab3.hs, and Tests.hs. The Lab3.hs file will have most of your code and should export functions for use in Tests.hs and Main.hs. The Main.hs file is your simple I/O loop. Follow the syntax from the first lab to understand modules (or check the course notes). Your Tests.hs should have sample runs implemented as tests, e.g.:

test1 = eval (Plus (Number 1) (Number 2)) == 3

and a final tests list:

tests = [test1, test2, ..., testn]
testAll = all id tests

You are primarily evaluated on whether you correctly implement the calculator.

Grading breakdown:

Extra Credit

For extra credit, implement the additional files Lab3Rational.hs and TestLab3Rational.hs which work with rational numbers. The inputs should be the same expressions but the internal values and the output are now rational numbers: so that the output for .75 would be 3 / 4. TestLab3Rational.hs should also throw an error properly if there is a division by zero. Ensure that the rational numbers in the output are in lowest terms.

This extra credit is worth at most 10%.