This is a zero-dependency, fully responsive scientific calculator built from scratch using TypeScript, HTML, and CSS. Instead of relying on methods like eval(), this project features a custom-built mathematical engine comprised of a Lexical Scanner, a Shunting Yard Parser, and a Stack-Machine Evaluator.
- Custom Math Engine: Safely parses and evaluates complex expressions (e.g.,
5 + 4 × sin(3 + 6) - 1) using Reverse Polish Notation (RPN). - Advanced Operations: Supports all major trigonometric functions, logarithms, factorials (!), permutations (nPr), combinations (nCr), and modulo operations.
- Different Angle Units: Supports the three major angle measuring units (degree, radian, and gradian).
- Continuous Calculation: Intelligently chains operations by appending or wrapping the previous result when a new operator or function is clicked.
- Dynamic Formatting: Automatically applies thousands separators (e.g.,
1,234,567.89) to both the active input and the final result to ensure readability. - Fluid & Responsive Design: Utilizes CSS
clamp()and media queries to seamlessly transition from a floating desktop widget to a full-screen, fluid mobile interface.
To run this project, you will need Node.js installed to compile the TypeScript file into JavaScript.
- Clone the repository and navigate into the project directory.
- In the project root directory run the following:
npx tsc- Once the
script.jsfile is generated in thedistfolder, simply openindex.htmlin any web browser to use the calculator.
When you open the calculator, you will see a standard scientific keypad.
- Basic Math: Click numbers and basic operators to form an expression.
- Advanced Math: Click the
⇄button to toggle the secondary keyboard layer, revealing hyperbolic trigonometric functions and inverse operations. - Angle Modes: Click the
Deg/Rad/Gradbutton to cycle through angle modes for trigonometric calculations. - Chaining: After pressing
=, you can immediately click an operator (like+) to continue adding to your result, or click a function (likesin) to wrap your result. - Editing: Use the backspace button to intelligently remove single characters or entire multi-character function blocks (e.g.,
sinh().
index.html: Defines the semantic structure of the calculator. It uses a flat button hierarchy within akeyboardgrid to allow for easy CSS Grid manipulation.style.css: Includes all the styling, and manages the "Fluid Design." It utilizes CSSclamp()for font sizes and button dimensions, ensuring the UI remains usable on various screen sizes.src/script.ts: The "brain" of the application. It contains the logic for state management, UI event listeners, and the core mathematical engine.
Once compilation is complete, a dist folder containing the compiled JavaScript and source maps is generated.
The calculator operates on a three-stage pipeline to transform a raw string of characters into a precise numerical result.
Before any math occurs, the raw string is scanned to identify "Tokens". The scanner iterates through the input and categorizes chunks into a Token interface:
interface Token {
type: 'Number' | 'BinaryOperator' | 'PrefixUnary' | 'PostfixUnary' | 'Constant' | ... ;
value: string;
}-
Multi-character Detection: The engine identifies functions like
asinh(ormodby checking for specific character buffers at the end of the string using a dedicated regex. -
Contextual Intelligence: It distinguishes between a Binary Minus (
$5 - 3$ ) and a Unary Minus ($-5$ ) by checking if the preceding token is an operator or a parenthesis. -
Automatic Multiplication: The scanner and click-handlers work together to inject implicit multiplication, turning
$5\pi$ or$2(3)$ into$5 \times \pi$ and$2 \times (3)$ .
Mathematical expressions are naturally written in Infix Notation (
-
Precedence & Associativity: The engine consults a internal map (
PRECEDENCE) to decide which operators "win." For example,$\times$ has higher precedence than$+$ , and exponentiation (^) is marked as right-associative. - The Stack & Queue: Operators are temporarily held on a stack, while operands are pushed to a postfix queue. Meeting a closing parenthesis triggers a stack "pop" until the expression is flattened into a linear, parenthesis-free sequence.
The final stage processes the RPN queue using a classic Stack Machine:
-
Operands: Numbers or constants (like
$\pi$ or$e$ ) are pushed onto the stack. -
Unary Operators: Functions like
$\sin$ or$!$ pop one value, apply the logic (including angle conversion for degrees, radians, or gradians), and push the result back. -
Binary Operators: Operators like
$+$ ,$nPr$ , or$mod$ pop two values, calculate the result, and push it back.
-
Chaining Calculations: The
isCalculatedstate allows the engine to either clear the screen for a new number or chain an operator (e.g.,$Ans + ...$ ) to the previous result. -
Precision Guarding: To prevent floating-point errors (e.g.,
$0.1 + 0.2$ ), the engine utilizes.toPrecision(15)andparseFloatbefore final formatting. -
Visual Formatting: The
formatExpressionhelper isolates visual formatting from backend logic, adding thousands-separators (commas) for readability without breaking the mathematical parser.
This project is open source and available under the MIT License. See the LICENSE file for more information.
