Today's exercise consists of a gentle introduction to the problems of parsing input text.
You are asked to write a calculator that deals with
S-expressions.
S-expressions
If you know Lisp, you already know what S-expressions are and you are probably ready to work on the exercise.
For those of you who don't, here's a (very) simple introduction.
S-expressions are nothing more than nested lists represented like this:
(elt1 elt2 elt3 ...)
where each element can be any of:
- an
atom.
- another S-expression.
Atoms are the primitives of the language. In the case of our calculator we will consider
- any integer
- fundamental arithmetic operations (+, -, *, /)
One more thing you need to know is that the first element of the expression will be considered the "operation" and the rest are the "operands" (or the arguments of the function depending how you look at it).
Let's take a look at some examples to make things clearer. Here are some valid S-expr (and their computed values as comments):
(+ 5 5) # equals 10
(+ (- 3 2) (* 9 2)) # equals 19
(/ 24 6 2) # equals 2
(/ 24 (/ 6 2)) # equals 8
As mentionned, the goal of the exercise is to write a program (in the language of your choice) that reads S-expressions and outputs their computed values.
A few notes:
- Operations can have any number of arguments.
- The order of evaluation matters for non-associative operations. Notice the difference between my 3rd and my 4th example.
- There is no limit to the depth of recursion (except the actual limits on your computer).
- We won't deal with error handling (invalid S-expr, division by zero, ...). You can assume that the input is always valid (let's not clutter the code uselessly).
- Whitespaces can be any number of space characters, tabs and newlines. The following is a perfectly valid (although terribly presented) S-expression:
(+ ( * 4 5)
( - 12 2)
(/ 45 (- 9
4))
(* (+ 100 2)
(/ 36 3)))
The presentation I did of S-expr is extremely "light". For a more rigorous definition, take a look at the Wikipedia article linked above. However, for the curious, here's a bit of formalism that's easy to grasp:
The above syntax is sometimes called "prefix notation" because the operator comes first. Similarly you can find "postfix notations" and "infix notations".
Here's the same expression expressed pre, in and postfix:
- prefix: + 1 2
- infix: 1 + 2
- postfix: 1 2 +
You may be familiar with infix notations exclusively, but it's good to know that the other two exist, and despite what you may think, are widely used in certain fields.
Bonus points
If you want to push the exercise further, here's what you can add to it:
- Proper error handling and displaying nice messages to the user in case of problems.
- Allow floating point numbers (like "5.3") as an atomic primitive of the language.
- Add more operators like power(^), modulo(%), trigonometric operations, ...
Final note
I hope the presentation of the problem is clear. If you have any doubt or any question do not hesitate to ask!