MIL-002: Add calculator core #31

Merged
Tirsvad merged 1 commits from mil-002-calculator-core into main 2026-10-04 14:51:03 +02:00
3 changed files with 153 additions and 0 deletions
Showing only changes of commit 5e1067b4f3 - Show all commits
+57
View File
@@ -0,0 +1,57 @@
"""!
@file constants.py
@brief Constants used by the console calculator.
"""
## Operation symbols, in the order they are offered to the user.
SYMBOL_ADD: str = "+"
SYMBOL_SUBTRACT: str = "-"
SYMBOL_MULTIPLY: str = "*"
SYMBOL_DIVIDE: str = "/"
## Answers accepted after a calculation.
ANSWER_CONTINUE: str = "y"
ANSWER_NEW: str = "n"
ANSWER_QUIT: str = "q"
## Prompts and messages shown to the user.
PROMPT_FIRST_NUMBER: str = "What's the first number?: "
PROMPT_NEXT_NUMBER: str = "What's the next number?: "
PROMPT_OPERATION: str = "Pick an operation: "
PROMPT_AGAIN: str = (
f"Type '{ANSWER_CONTINUE}' to continue calculating with {{result}}, "
f"'{ANSWER_NEW}' to start a new calculation, "
f"or '{ANSWER_QUIT}' to quit: "
)
MESSAGE_INVALID_NUMBER: str = "That is not a number, please try again."
MESSAGE_INVALID_OPERATION: str = "That is not a valid operation, please try again."
MESSAGE_INVALID_ANSWER: str = "Please answer with one of the listed letters."
MESSAGE_DIVIDE_BY_ZERO: str = "You cannot divide by zero."
MESSAGE_GOODBYE: str = "Goodbye!"
## ASCII title shown when the program starts.
ASCII_TITLE: str = r"""
____ _ _ _
/ ___|__ _ | | ___ _ _| | __ _| |_ ___ _ __
| | / _` | | |/ __| | | | |/ _` | __/ _ \| '__|
| |__| (_| | | | (__| |_| | | (_| | || (_) | |
\____\__,_| |_|\___|\__,_|_|\__,_|\__\___/|_|
"""
## ASCII calculator shown under the title.
ASCII_CALCULATOR: str = r"""
_____________________
| _________________ |
| | 0 | |
| |_________________| |
| ___ ___ ___ ___ |
| | 7 | 8 | 9 | | + | |
| |___|___|___| |___| |
| | 4 | 5 | 6 | | - | |
| |___|___|___| |___| |
| | 1 | 2 | 3 | | x | |
| |___|___|___| |___| |
| | . | 0 | = | | / | |
| |___|___|___| |___| |
|_____________________|
"""
+63
View File
@@ -0,0 +1,63 @@
"""!
@file operations.py
@brief Arithmetic operations and the dictionary that maps symbols to them.
"""
from collections.abc import Callable
from calc.constants import (
SYMBOL_ADD,
SYMBOL_DIVIDE,
SYMBOL_MULTIPLY,
SYMBOL_SUBTRACT,
)
def add(n1: float, n2: float) -> float:
"""!
@brief Add two numbers.
@param n1 The first number.
@param n2 The second number.
@return The sum of @p n1 and @p n2.
"""
return n1 + n2
def subtract(n1: float, n2: float) -> float:
"""!
@brief Subtract the second number from the first.
@param n1 The first number.
@param n2 The second number.
@return The difference @p n1 minus @p n2.
"""
return n1 - n2
def multiply(n1: float, n2: float) -> float:
"""!
@brief Multiply two numbers.
@param n1 The first number.
@param n2 The second number.
@return The product of @p n1 and @p n2.
"""
return n1 * n2
def divide(n1: float, n2: float) -> float:
"""!
@brief Divide the first number by the second.
@param n1 The dividend.
@param n2 The divisor.
@return The quotient @p n1 divided by @p n2.
@throws ZeroDivisionError When @p n2 is zero.
"""
return n1 / n2
## Maps an operation symbol to its function. The functions are stored, not called.
OPERATIONS: dict[str, Callable[[float, float], float]] = {
SYMBOL_ADD: add,
SYMBOL_SUBTRACT: subtract,
SYMBOL_MULTIPLY: multiply,
SYMBOL_DIVIDE: divide,
}
+33
View File
@@ -0,0 +1,33 @@
"""!
@file test_operations.py
@brief Tests for the arithmetic operations.
"""
import pytest
from calc.operations import OPERATIONS, add, divide, multiply, subtract
def test_add_returns_sum() -> None:
assert add(2, 3) == 5
def test_subtract_returns_difference() -> None:
assert subtract(2, 3) == -1
def test_multiply_returns_product() -> None:
assert multiply(4, 2.5) == 10
def test_divide_returns_quotient() -> None:
assert divide(9, 3) == 3
def test_divide_when_divisor_is_zero() -> None:
with pytest.raises(ZeroDivisionError):
divide(1, 0)
def test_operations_maps_symbols_to_functions() -> None:
assert OPERATIONS == {"+": add, "-": subtract, "*": multiply, "/": divide}