Skip to content

Tracking Issue: Fully-featured math expression parser and calculator #2026

Open
1 of 2 issues completed
Open
@Keavon

Description

@Keavon

This should be built as a library (in our /libraries directory) that uses a parsing framework such as Chumsky that takes a string at runtime and calculates its result, including units and dimensional analysis.

This would make a great solo contribution for somebody interested in developing a robust solution for this problem without needing to learn any of Graphite's code base. It would also make a good university team/solo "capstone" project, which our organization can serve as an "industry sponsor" for.

Here's the spec:

Operators

  • Infix operators:
    • Addition: +
    • Subtraction: -
    • Multiplication: *
    • Division: /
    • Modulo: %
    • Exponentiation: ^
    • Equals: ==
    • Not Equals: !=
    • Less Than or Equal To: <=,
    • Greater Than or Equal To: >=,
    • Less Than: <
    • Greater Than: >
    • Or: ||
    • And: &&
  • Prefix operators:
    • Unary Plus: +
    • Unary Minus: -
    • Square root:
    • Not:!
  • Postfix operators:
    • Factorial: !
  • Grouping operators:
    • Parentheses: ( and )
  • Logical operators:
    • Equality: ==, !=, >=, <=, <, >, ||, &&, and ! logical operators to the spec.

Constants

  • Infinity: inf, INF, infinity, INFINITY,
  • Imaginary unit: i, I
  • Pi: pi, PI, π
  • Tau: tau, TAU, τ
  • Euler's Number: e
  • Golden Ratio: phi, PHI, φ
  • Earth's gravitational acceleration: G

Variables

  • Single-letter variables (examples: x, y, z)
  • Multi-letter variables (examples: theta, alpha, beta, gamma)
  • Non-Latin letters and symbols (examples: λ, , א, 👍)

Functions

  • Trig:
    • sin(), cos(), tan()
    • csc(), sec(), cot()
  • Inverse trig (aliases: ar- and arc- prefixes):
    • asin(), acos(), atan(), atan2()
    • acsc(), asec(), acot()
  • Hyperbolic:
    • sinh(), cosh(), tanh()
    • csch(), sech(), coth()
  • Inverse hyperbolic (aliases: ar- and arc- prefixes):
    • asinh(), acosh(), atanh()
    • acsch(), asech(), acoth()
  • Logarithm:
    • Natural log: ln()
    • Logarithm base 10: log()
    • Logarithm base 2: log2()
    • Logarithm base N: logN() (alias: log_N())
      • Examples: log5(), log3.25(), log_8()
    • Logarithm with variable base: log(x, b)
  • Exponential:
    • e^x: exp()
    • 2^x: exp2()
  • Roots:
    • Square root: sqrt() (alias: root(), root2(), root_2())
    • Cube root: cbrt() (alias: root3(), root_3())
    • Nth root: rootN() (alias: root_N())
      • Examples: root3(), root5.237(), root_27.72()
    • Root with variable degree: root(n, x)
      • Example: root(27, 3) is equivalent to cbrt(27)
  • Geometry:
    • Hypotenuse equation sqrt(a^2 + b^2): hypot()
  • Mapping:
    • Absolute value: abs() (magnitude of a real or complex number)
    • Floor: floor()
    • Ceiling: ceil()
    • Round: round()
    • Clamp: clamp(x, min, max)
    • Lerp: lerp(a, b, t)
    • Truncate: trunc()
    • Fractional part: frac()
    • Sign: sign()
  • Logical
    • Is NaN: isnan() (returns 1 if the operand is NaN, 0 otherwise)
    • Equality: eq() (returns 1 if both operands and units are equal, 0 otherwise)
    • Greater than: greater() (returns 1 if the left operand is greater than the right operand and units are equal, 0 otherwise)
    • Greater-or-equal: greatereq() (returns 1 if the left operand is greater than or equal to the right operand and units are equal, 0 otherwise)
    • Less than: lesser() (returns 1 if the left operand is less than the right operand and units are equal, 0 otherwise)
    • Less-or-equal: lessereq() (returns 1 if the left operand is less than or equal to the right operand and units are equal, 0 otherwise)
    • Not: not() (returns 1 if the operand is zero, 0 otherwise)
    • And: and() (returns 1 if both operands are non-zero, 0 otherwise)
    • Or: or() (returns 1 if either operand is non-zero, 0 otherwise)
    • Xor: xor() (returns 1 if exactly one operand is non-zero, 0 otherwise)
    • If: if(cond, a, b) (returns a if cond is non-zero, b otherwise)
      • A potential alternative to nonzeroness in these logical functions could be using true and false as units with either no value, a value of 0, or a value of 1
  • Complex numbers:
    • Real part: real()
    • Imaginary part: imag()
    • Angle: arg() (alias: angle())

Number representations

  • Integers (examples: 0, 42, -42)
  • Decimals (examples: 0.5, 3.14159, -2.71828)
  • Scientific notation (examples: 1e-6, 2.5E3)
  • Imaginary numbers (examples: 3i, -2.5i, 1.5e-3i, 2.5e3 + 2.1e-2i)
  • Units (examples: 5m, 5 m, 3.5kg, 3.5 kg, 2.5m/s^2, 2.5 m/s^2)

Units

  • In addition to the unit abbreviations, full unit names can be used in either singular or plural form
  • Math expressions perform dimensional analysis
    • The scalar part is simplified while the unit part is kept as a fraction if it cannot be simplified
    • Units should only be combined if they are mixed with other units in either the numerator or denominator, defaulting to the unit that would best avoid loss of precision
      • Example: 5m + 3m -> 8m
      • Example: 5ft + 3ft -> 8ft
      • Example: 5m + 3ft -> 5.9144m (meters are chosen because 1ft = 0.3048m exactly, while its inverse can't be represented exactly)
  • Values can always be requested for conversion to a specific unit in the API

Unit list

  • Unitless
  • Pixels:
    • px
  • Length:
    • Meter m and its prefixes
    • thou, pc, pt, in/", ft/', yd, mi, nmi
  • Area:
    • length^2
    • acre, are, hectare
  • Volume:
    • length^3, area * length
    • Paper sizes: A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10
    • floz, cup, pint, qt, gal
    • l and its prefixes
  • Mass:
    • Kilogram kg and its prefixes
    • gr, oz, lb, ton, tonne
  • Time:
    • Second s and its prefixes
    • min, hr, day
  • Angle:
    • Degrees: deg/°
    • Radians: rad (unitless is not an angle)
    • Turns: turn
  • Velocity:
    • length / time
    • m/s, km/h, mph, knot
  • Acceleration:
    • velocity / time, length / time^2
    • m/s^2, ft/s^2, g
  • Angular velocity:
    • angle / time
    • deg/s, rad/s, rpm, rps
  • Angular acceleration:
    • angular velocity / time, angle / time^2
    • deg/s^2, rad/s^2
  • Illumination
    • TODO: Research and lay out what these are, like candela, nits, etc.

Unit metric prefixes

  • Nano (n)
  • Micro (µ, u)
  • Milli (m)
  • Centi (c)
  • Deci (d)
  • Deca (da)
  • Hecto (h)
  • Kilo (k)
  • Mega (M)
  • Giga (G)
  • Tera (T)

Implicit multiplication

  • Automatically handle multiplication without explicit * operator
    • Between numbers and variables/constants (examples: 3x, 2pi)
    • Between numbers and functions (example: 4sin(90deg))
    • Between adjacent variables/constants (examples: xy, pi r^2)

API features

  • Parsing/construction separate from evaluation so the same expression can be run repeatedly with different variables without wasted performance
  • Setting the result of one expression to a variable for use in subsequent expressions
  • Custom supplied units, unit symbols, functions, and constants
  • Syntax highlighting

Sub-issues

Metadata

Metadata

Assignees

Labels

Projects

Status

Ongoing Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions