Programming Language Design

Building a Calculator


Learning Objectives

  • You know the components usually needed for a calculator.
  • You can implement a simple evaluation function recursively.

Calculator 🧮

Now that we have an abstract syntax tree, we can define evaluation semantics for the language — let’s do a calculator!

calculator:StringOption()

Components we need for a calculator:

  1. An abstract representation for the arithmetic language used as the input, and its parser.
  2. A function which computes the value of the parsed term — the evaluator.
  3. A way to get user input in order to make the calculator interactive, also known as a REPL.

Using the parser and the evaluator we can define the calculator simply as

calculator:StringOption()calculator𝑠=parser𝑠|>mapeval

Here, map:(𝑎𝑏)Option𝑎Option𝑏 is used to lift the eval:Term to a function Option(Term)Option(). Calling the resulting function with parser𝑠:Option(Term) produces an Option().

Evaluating Terms to Values

Let’s define the implementation of the evaluation function mathematically.

eval:Term

As a reminder, here are all the constructors for Term:

Add:(Term,Term)TermSub:(Term,Term)TermMul:(Term,Term)TermNum:Term

To implement the function, we can assume the argument is a Term, so let’s do case analysis on the term, breaking it into four cases for each of the four constructors:

eval(Add(𝑡1,𝑡2))=?eval(Sub(𝑡1,𝑡2))=?eval(Mul(𝑡1,𝑡2))=?eval(Num(𝑛))=?

Here, the ? is used as a notation for a “todo”, i.e. a missing implementation, in math.

By matching the values 𝑡1 and 𝑡2 with the data constructors, we note that they are of type Term also. Similarly we see that 𝑛:. The holes (?) have type .

First, we obviously want evaluation of Num(5) to result in a 5, and likewise for other integers, so we can fill the final hole with 𝑛.

eval(Add(𝑡1,𝑡2))=?eval(Sub(𝑡1,𝑡2))=?eval(Mul(𝑡1,𝑡2))=?eval(Num(𝑛))=𝑛

Next, if we think about the term Add(Num(1),Num(2)), the evaluation function should result in a 3, but how? Well, we can recursively evaluate 𝑡1 and 𝑡2 as we know, by the imaginary induction hypothesis, that eval(𝑡1) and eval(𝑡1) result in the correct results. Then it’s just a matter of matching the constructor with the mathematical operator.

eval(Add(𝑡1,𝑡2))=eval(𝑡1)+eval(𝑡2)eval(Sub(𝑡1,𝑡2))=eval(𝑡1)eval(𝑡2)eval(Mul(𝑡1,𝑡2))=eval(𝑡1)eval(𝑡2)eval(Num(𝑛))=𝑛

Notice that this evaluation function didn’t require any error handling and there was never any ambiguity about which number to produce. Also, the evaluation is total, as it always a value for every input term.

Now your task is to translate this mathematical implementation into Gleam.

Loading Exercise...

Finally, implement the calculator.

Loading Exercise...