dc: The Calculator as a Programming Language for Developers
dc is a classic Unix calculator, older than the C language, included in standard distributions of most systems, including macOS. Behind its simple facade lies a full-fledged stack-based programming language with registers, macros, conditional constructs, and recursion. The core model is the stack: numbers and strings are pushed onto the stack via commands, operators pop the top elements, perform an action, and return the result.
First calculation: to sum 2 + 2, enter 2 2 + p. Here, 2 2 pushes the numbers onto the stack line by line, + adds the top two elements, and p prints the result.
2 2 +
p
4
The stack is the key element: every literal (number or [string]) pushes a value onto the top. Comments start with #.
Working with Registers and Variables
dc provides 256 registers (a-z, 0-9, and other characters) that store numbers or strings. Commands sR save the top of the stack into register R, and lR load it back. Registers maintain state independently of the main stack.
Example of saving and restoring a string:
[Hello world!] sx
lx
p
Hello world!
Registers are suitable for global variables. Numbers in registers are manipulated similarly to the stack: li loads i onto the stack.
Macros as Functions
Functions in dc are macros: strings stored in registers, executed with the command lRx, where x is the register. Macros are defined as [code] sR.
Example of a function to sum registers a and b:
[la lb +] sS
1 sa
2 sb
lSx
p
3
Macros support recursion and can call each other, forming subroutines.
Conditional Constructs
Branching is based on comparing the top two elements of the stack. Operators include =, >, <, !=, and others. If the condition is true, the macro from the specified register is executed.
[[Equal] p] sE
[[Non equal] p] sN
1 0
=E
1 0
!=N
Non equal
Comparisons consume two elements from the stack, leaving the result (0 or 1).
Recursive Loops
There are no loops, but recursion is implemented via macro self-calls. Example of printing numbers from 1 to 10:
[
li p
1 + si
li 11 >C
] sC
1 si
lCx
Macro C prints i, increments it, and recursively calls itself while i < 11. The current i is already on the stack upon entry.
- Advantages of recursion in dc: simplicity, no explicit loops.
- Limitations: risk of stack overflow with deep recursion.
- Alternatives: tail recursion minimizes stack usage.
FizzBuzz in dc
Implementation of FizzBuzz up to 100 demonstrates a combination of macros, conditions, and recursion. Key commands: P (print without newline), c (clear stack).
Full code:
[[Fizz] P 0 sd] sF
[[Buzz] P 0 sd] sB
[li 3 % 0 =F li 5 % 0 =B] sW
[10 P] sP
[li p c] sD
[
1 sd
lWx
ld 0 =P
ld 1 =D
li 1 + si
li 101 >M
] sM
1 si
lMx
Logic: flag d determines whether to print the number. Macro W checks divisibility by 3/5, setting Fizz/Buzz. P with 10 (LF) for a new line.
Advanced Applications: Peg Solitaire
dc allows solving complex tasks, such as Peg Solitaire—a puzzle with peg jumps on a board. The board state is encoded as a binary number, with manipulations via arithmetic and bitwise operations (shifts and masks are available).
Move validation formulas in RPN look complex due to bitwise checks but enable a complete game: from the initial configuration to the final peg.
This approach showcases dc as a tool for algorithm prototyping without compilation.
Key Takeaways
- The stack is the foundation of all operations: literals push, operators pop/push results.
- Registers (256 total) for state, macros for subroutines with
lRx. - Conditions consume the stack, recursion replaces loops.
p/Pfor output,cfor clearing,%for modulus.- Suitable for scripting, prototyping, and learning RPN.
— Editorial Team
No comments yet.