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:Stringโ†’Option(โ„ค)

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:Stringโ†’Option(โ„ค)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...