A flexible calculator that ships with a Windows GUI front end and a cross-platform CLI. Both layers share the same C parser so complex expressions behave identically. The calculator now includes a modular converter system for unit conversions that can be used with the command-line engine.
All platforms require the GNU compiler gcc.
Windows note: the easiest path to
gccis through Cygwin.
The CLI can be compiled on macOS. Install the toolchain via Homebrew:
brew install gccThe CLI runs on common Linux distributions. Install the essentials with apt:
sudo apt update
sudo apt install build-essentialTo write changes to help.txt, you'll need xxd.
sudo apt install xxdFirst, use Windows Package Manager (winget) to install Cygwin:
winget install Cygwin.CygwinNext, download the Cygwin installer setup-x86_64.exe. Move it to
/usr/bin (or another location on your PATH) so it can be invoked from the command prompt, then
install gcc:
setup-x86_64.exe -P gcc-coreTo write changes to help.txt, you'll need xxd.
setup-x86_64.exe -P xxdFollow the installer prompts until you reach Select Packages.
If
gcc-coreis not listed, search for it, mark it for installation, then continue.
The CLI builds on macOS, Linux, and Windows. To compile with the converter module:
This method embeds the JSON rules into the executable, so it works from any directory:
# First, generate all embedded rule headers automatically
.\build_rules.bat # Windows
# or
./build_rules.sh # macOS/Linux
# Then compile with embedded rules flag
gcc -DUSE_EMBEDDED_RULES ccal.c modules/converter.c -o ccal.exeThe build script automatically processes all JSON files in rules/converter/ and generates:
- Individual header files:
modules/length_rules.h,modules/temperature_rules.h, etc. - Master header:
modules/rules.hthat includes all rule headers
This method loads rules from JSON files at runtime:
gcc ccal.c modules/converter.c -o ccal.exeNote: Without -DUSE_EMBEDDED_RULES, you must run ccal from the project directory where rules/converter/ is accessible.
To compile basic calculator only:
gcc ccal.c -o ccal.exeTo include updates to help.txt, regenerate the header before compiling:
xxd -i help.txt > help.hThis embeds the latest help text in the executable.
Start by cloning the code and switching into the working directory:
git clone https://github.com/jhauga/ccal.git
cd ccalCompile the GUI without bundling extra resources:
gcc -DBUILDING_GUI ccal.c ccal_gui.c -o ccal_gui.exe -mwindowsFirst compile the resource script into a COFF object:
windres ccal_gui.rc -O coff -o ccal_gui.resThen link the GUI, resource object, and core engine together:
gcc -DBUILDING_GUI ccal_gui.c ccal.c ccal_gui.res -o ccal_gui.exe -mwindows- Launch
ccal_gui.exefrom Explorer or the command line. - Enter expressions using the on-screen buttons or the keyboard.
- Press
Enteror click=to evaluate. - Press
dto clear the current expression and result. - Use
Ctrl + Cto copy the result to the clipboard.
To use the CLI, pass digits, arithmetic operators, and nesting characters ((, ), {, }, [, ]) separated with spaces.
Important: use
xinstead of*, andpinstead of^, unless you pass-q/--quote.
+ -> addition
- -> subtraction
/ -> division
x -> multiplication
* -> multiplication (use -q/--quote)
p -> exponentiation (power of)
^ -> exponentiation (use -q/--quote)
To use the command line tool either input all elements of the equation separated with a space
character, or use the quote option -q, --quote which does not require elements to be separated
with a space character.
If nested brackets cause argument parsing issues, escape them or wrap the expression in quotes. Square brackets generally require the fewest escapes.
Add two numbers:
> ccal 1 + 1
> 2Perform a complex equation:
> ccal [ [ 34 x 11 ] / [ 10 - 5 ] ] - [ 4 x 53 x [ 30 / 10 ] ]
> -561.2Without
-q/--quote, order of operations can break across spaced tokens, so expressions like:
> ccal [ [ 34 x 11 ] / [ 10 - 5 ] ] - [ 4 x 53 x [ 30 / 10 ] + [ 2 x [ 40 - 20 ] ] ] / [ 8 x 6 x [ 12 / 2 ] + 4 ]
> -2.058904109589041will evaluate incorrectly (the correct answer is 72.4849). Use -q/--quote—recommended—or add explicit parentheses to force the intended precedence:
Quote option (recommended):
> ccal -q "[[34x11]/[10-5]]-[4x53x[30/10]+[2x[40-20]]]/[8x6x[12/2]+4]"
> 72.48493150684931Manual grouping:
> ccal [ [ 34 x 11 ] / [ 10 - 5 ] ] - [ [ [ 4 x 53 ] x [ 30 / 10 ] + [ 2 x [ 40 - 20 ] ] ] / [ 8 x 6 x [ 12 / 2 ] + 4 ] ]
> 72.48493150684931Both approaches return the correct value.
Terminals treat commas as separators, so wrap comma-separated numbers in quotes or use
-q/--quote. Instead of:
> ccal 1,000 - 1
> Error: Invalid expressionUse the quote option or wrap comma numbers manually:
Quote option:
> ccal --quote "2,000-1,000"
> 1000Manual quoting:
> ccal "2,000" - "1,000"
> 1000to get the correct calculation.
To calculate exponentiation, use p as the operator. With -q/--quote, you can also use ^.
> ccal 2 p 2
> 4
> ccal --quote "2^2"
> 4The ccal calculator includes a modular converter system for unit conversions. The converter is extensible and rule-based, allowing users to create custom conversion rules.
The converter module is integrated directly into the ccal command-line tool. Use the module flag to perform conversions:
Syntax:
# With explicit rule name
ccal [/M|-m|--module] <module> <rule> <value> <from_unit> <to_unit>
# With auto-detection (embedded rules only)
ccal [/M|-m|--module] <module> <value> <from_unit> <to_unit>Examples:
# Explicit rule name
ccal -m converter length 10 in cm
# Output: 10.000000 in = 25.400000 cm
# Auto-detection (when using embedded rules)
ccal -m converter 10 in cm
# Output: 10.000000 in = 25.400000 cm
# Temperature conversion with auto-detection
ccal -m converter 100 C F
# Output: 100.000000 C = 212.000000 F
# Convert 5 feet to meters using /M flag
ccal /M converter 5 ft m
# Output: 5.000000 ft = 1.524000 m
# Convert 100 kilometers to miles using --module flag
ccal --module converter length 100 km mi
# Output: 100.000000 km = 62.137100 miThe integrated converter automatically:
- Loads conversion rules from embedded data (when compiled with
-DUSE_EMBEDDED_RULES) - Auto-detects the appropriate rule based on units (rule name optional)
- Performs the conversion calculation
- Formats and displays the result
Auto-Detection: When using embedded rules, the rule name is optional. The converter will automatically detect which rule to use based on the units:
# These are equivalent:
ccal -m converter length 10 in cm
ccal -m converter 10 in cm
# Both work for temperature too:
ccal -m converter temperature 100 C F
ccal -m converter 100 C FThe converter system uses:
modules/- Contains converter implementation codeconverter.c- Core conversion engineconverter.h- Function declarations and structureslength_rules.h,temperature_rules.h- Embedded rules (generated from JSON)rules.h- Master header that includes all rule files
rules/- Contains JSON rule files for different conversion typesconverter/length.json- Length/distance conversion rulesconverter/temperature.json- Temperature conversion rules- Users can add custom rule files following the same format
Embedded Rules: When compiled with -DUSE_EMBEDDED_RULES, all JSON rules in rules/converter/ are embedded directly into the executable using generated header files. This allows the converter to:
- Work from any directory without needing access to external JSON files
- Auto-detect which rule to use based on the units provided
- Support multiple conversion types (length, temperature, etc.) seamlessly
Use the build_rules.bat (Windows) or build_rules.sh (macOS/Linux) script to automatically generate headers for all rules.
Conversion rules are defined in JSON files with the following structure:
{
"converter": ["mm", "cm", "m", "km", "in", "ft", "yd", "mi"],
"length": [
{
"name": "in,inch",
"to": [25.4, 2.54, 0.0254, 0.0000254, 1, 0.0833, 0.0278, 0.0000158]
}
]
}converter: Array of unit abbreviations in ordername: Comma-separated unit names/aliases (case-insensitive)- Creates CLI flags:
-in,--inch - Creates GUI label:
inch(in)
- Creates CLI flags:
to: Conversion factors in the same order as theconverterarray
The converter is integrated into ccal. To compile with embedded rules:
# Generate all rule headers automatically
.\build_rules.bat # Windows
./build_rules.sh # macOS/Linux
# Compile with embedded rules
gcc -DUSE_EMBEDDED_RULES ccal.c modules/converter.c -o ccal.exeOr compile with external rule files:
gcc ccal.c modules/converter.c -o ccal.exeAdding New Rules: To add a new conversion type:
- Create a JSON file in
rules/converter/(e.g.,weight.json) - Run the build script:
.\build_rules.bator./build_rules.sh - Add the new rule name to the
rule_names[]array inauto_detect_rule()function in modules/converter.c - Add a corresponding case in
load_embedded_conversion_rules()function - Recompile with
-DUSE_EMBEDDED_RULES
If you compiled the standalone converter tool, you can use it separately:
Convert a value from one unit to another:
> converter 10 inch cm
> 10.0000 in = 25.400000 cmConvert and display all available units:
> converter 5 ft --all
> 5.0000 ft =
> mm : 1524.000000
> cm : 152.400000
> m : 1.524000
> km : 0.001524
> in : 60.000000
> ft : 5.000000
> yd : 1.665000
> mi : 0.000945The length converter supports the following units with flexible naming:
- Millimeter:
mm,millimeter→-mm,--millimeter - Centimeter:
cm,centimeter→-cm,--centimeter - Meter:
m,meter→-m,--meter - Kilometer:
km,kilometer→-km,--kilometer - Inch:
in,inch→-in,--inch - Foot:
ft,foot→-ft,--foot - Yard:
yd,yard→-yd,--yard - Mile:
mi,mile→-mi,--mile
The temperature converter supports the following units:
- Celsius:
C,celsius→-C,--celsius - Fahrenheit:
F,fahrenheit→-F,--fahrenheit - Kelvin:
K,kelvin→-K,--kelvin
Temperature conversions use both multiplication factors and offsets to handle the different scales correctly.
All unit names are case-insensitive and support multiple aliases.
Users can create their own conversion rules by adding JSON files to the rules/converter/ directory. Follow this syntax structure:
{
"converter": ["unit1", "unit2", "unit3", ...],
"rule_name": [
{
"name": "unit_name,alias1,alias2",
"to": [factor_to_unit1, factor_to_unit2, factor_to_unit3, ...],
"offset": [offset_for_unit1, offset_for_unit2, offset_for_unit3, ...]
}
]
}Required Fields:
-
"converter": Array of unit abbreviations in the order they appear in conversion arrays- Example:
["mm", "cm", "m", "km"]for length - These are the short names displayed in output
- Example:
-
"rule_name": Array of conversion unit definitions (use the rule file name without.json)- Must match your filename:
weight.json→"weight": [...]
- Must match your filename:
-
"name": Comma-separated aliases for each unit (case-insensitive)- First name is typically the primary identifier
- Additional names are alternative ways to reference the same unit
- Example:
"in,inch,inches"allows users to type any of these
-
"to": Array of conversion factors from this unit to each unit inconverterarray- Length must match
converterarray length - Order must match
converterarray order - Each value converts 1 unit of the current unit to the corresponding target unit
- Example: For inches,
[25.4, 2.54, 0.0254]converts to [mm, cm, m]
- Length must match
Optional Fields:
"offset": Array of offset values for conversions (typically used for temperature scales)- Use when conversion requires:
result = (value * factor) + offset - If omitted, offset defaults to 0 for all conversions
- Example: Temperature conversions like Celsius to Fahrenheit need both factor and offset
- Use when conversion requires: