100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Building a Simple Calculator in LISP

A hands-on walkthrough of building a working arithmetic expression calculator in Common Lisp, from tokenizing input to evaluating nested expressions.

PracticeBeginner10 min readJul 10, 2026
Analogies

Building a Simple Calculator in LISP

Because arithmetic operators in LISP are just ordinary function calls written in prefix S-expression form, such as (+ 1 2) or (* 3 (+ 4 5)), building a calculator is one of the most natural first projects in the language: you are essentially writing a function that evaluates an already-parsed S-expression tree, which is close to what the LISP reader does internally when it reads your source code. This walkthrough builds a calculator that supports the four basic operators plus nested expressions and simple error handling for cases like division by zero.

🏏

Cricket analogy: Building a calculator in LISP is like scoring a match using a structured scorecard app where each entry (runs, wickets) already matches the app's internal data model, so displaying the final total requires almost no extra translation work.

Representing and Evaluating Expressions

A calculator expression like 3 + 4 * 2 can be represented directly as a LISP S-expression (+ 3 (* 4 2)), where the operator is the first element (the head) and the operands follow as the rest of the list. Evaluating this structure recursively is straightforward: if the expression is a number, return it as-is; otherwise, dispatch on the operator symbol at the head of the list, recursively evaluate each operand, and apply the corresponding arithmetic function—this recursive-descent pattern mirrors exactly how LISP's own eval function processes any S-expression.

🏏

Cricket analogy: Evaluating nested expressions recursively is like calculating a team's net run rate, where you first resolve each innings' sub-total before combining them into the final aggregate figure, working from the smallest unit outward.

lisp
;; A recursive evaluator for arithmetic S-expressions
(defun calc-eval (expr)
  (cond
    ((numberp expr) expr)
    ((eq (first expr) '+) (+ (calc-eval (second expr)) (calc-eval (third expr))))
    ((eq (first expr) '-) (- (calc-eval (second expr)) (calc-eval (third expr))))
    ((eq (first expr) '*) (* (calc-eval (second expr)) (calc-eval (third expr))))
    ((eq (first expr) '/)
     (let ((divisor (calc-eval (third expr))))
       (if (zerop divisor)
           (error "Division by zero in expression: ~A" expr)
           (/ (calc-eval (second expr)) divisor))))
    (t (error "Unknown operator: ~A" (first expr)))))

;; 3 + 4 * 2  =>  (+ 3 (* 4 2))
(calc-eval '(+ 3 (* 4 2))) ;; => 11

Parsing an Infix String into an S-Expression

If the goal is to accept ordinary infix input like the string "3 + 4 * 2" rather than a pre-built S-expression, the calculator needs a small tokenizer and a precedence-aware parser before evaluation can happen: tokenizing splits the string into symbols like 3, +, 4, *, 2, and a recursive-descent parser then groups higher-precedence operators like * and / more tightly than + and -, producing the nested list structure (+ 3 (* 4 2)) shown earlier. This two-stage design—parse to a tree, then evaluate the tree—is the same architecture used by full-scale interpreters and compilers, just at a much smaller scale.

🏏

Cricket analogy: Tokenizing and parsing infix input is like a scorer converting a commentator's spoken description into structured scorecard entries before any statistics can be calculated from it.

You don't need a full tokenizer/parser to get started: Common Lisp's own reader already parses S-expression syntax for you, so (read-from-string "(+ 3 (* 4 2))") gives you a ready-to-evaluate list directly, which is why many beginner LISP calculators skip infix parsing entirely and just accept S-expression input.

Handling Errors and Edge Cases

A production-quality calculator needs to handle division by zero, unknown operators, and malformed input gracefully rather than crashing with an unhelpful stack trace; Common Lisp's error function signals a condition that can be caught with handler-case, letting the calling code decide whether to report a friendly message, retry with different input, or propagate the failure. Testing edge cases explicitly—(calc-eval '(/ 5 0)), (calc-eval '(% 5 2)) with an undefined operator, or deeply nested expressions—during development catches bugs that simple happy-path testing would miss.

🏏

Cricket analogy: Handling division by zero gracefully is like a scoring app catching the edge case of a team being all out for zero without bowling a single over, displaying 'N/A' instead of crashing on an undefined average calculation.

Common Lisp's / function returns an exact rational number for integer division, so (/ 7 2) evaluates to 7/2, not 3.5 or 3. If your calculator is meant to behave like a typical decimal calculator, explicitly coerce results with (float (/ 7 2)) or use / with at least one floating-point argument to get 3.5.

  • Arithmetic expressions map naturally onto LISP S-expressions since operators are just prefix function calls, e.g. (+ 3 (* 4 2)).
  • A recursive evaluator dispatches on the operator symbol at the head of each sub-list and recursively evaluates operands.
  • Parsing infix strings into S-expressions requires a tokenizer and a precedence-aware recursive-descent parser, but read-from-string can bypass this if you accept S-expression input directly.
  • Use error and handler-case to gracefully handle division by zero, unknown operators, and malformed input.
  • Common Lisp's / returns exact rationals for integer arguments; coerce to float explicitly if decimal output is expected.
  • Testing edge cases like zero divisors and unknown operators during development catches bugs missed by happy-path testing alone.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#LISPStudyNotes#BuildingASimpleCalculatorInLISP#Building#Simple#Calculator#LISP#StudyNotes#SkillVeris#ExamPrep