README.md

May 4, 2026 · View on GitHub

val

release crates.io CI docs.rs dependency status

val (eval) is a simple arbitrary precision calculator language built on top of chumsky and ariadne.

val

Installation

val should run on any system, including Linux, MacOS, and the BSDs.

The easiest way to install it is by using cargo, the Rust package manager:

cargo install val

Otherwise, see below for the complete package list:

Cross-platform

Package Manager Package Command
Cargo val cargo install val
Homebrew terror/tap/val brew install terror/tap/val

Pre-built binaries

Pre-built binaries for Linux, MacOS, and Windows can be found on the releases page.

Usage

The primary way to use val is via the provided command-line interface. There is currently ongoing work on a Rust library and web playground, which will provide a few extra ways to interact with the runtime.

Below is the output of val --help, which describes some of the arguments/options we support:

val 0.3.6
Liam <liam@scalzulli.com>
An arbitrary precision calculator language

Usage: val [OPTIONS] [FILENAME]

Arguments:
  [FILENAME]  File to evaluate

Options:
  -e, --expression <EXPRESSION>        Expression to evaluate
  -l, --load <LOAD>                    Load files before entering the REPL
  -p, --precision <PRECISION>          Binary precision (bits) to use for calculations [default: 1024]
  -r, --rounding-mode <ROUNDING_MODE>  Rounding mode to use for calculations [default: to-even]
      --stack-size <STACK_SIZE>        Stack size in MB for evaluations [default: 128]
  -h, --help                           Print help
  -V, --version                        Print version

Running val on its own will spawn a repl (read–eval–print loop) environment, where you can evaluate arbitrary val code and see its output immediately. We use rustyline for its implementation, and we support a few quality of life features:

  • Syntax highlighting (see image above)
  • Persistent command history
  • Emacs-style editing support by default
  • Filename completions
  • Hints (virtual text pulled from history)

The val language supports not only expressions, but quite a few statements as well. You may want to save val programs and execute them later, so the command-line interface provides a way to evaluate entire files.

For instance, lets say you have the following val program at factorial.val:

fn factorial(n) {
  if (n <= 1) {
    return 1
  } else {
    return n * factorial(n - 1)
  }
}

println(factorial(5));

You can execute this program by running val factorial.val, which will write to standard output 120.

Lastly, you may want to evaluate a val expression and use it within another program. The tool supports executing arbitrary expressions inline using the --expression or -e option:

val -p 53 -e 'sin(2) * e ^ pi * cos(sum([1, 2, 3]))'
20.203684508229124193

n.b. The --expression option and filename argument are mutually exclusive.

Features

This section describes some of the language features val implements in detail, and should serve as a guide to anyone wanting to write a val program.

Statements

val supports a few statement constructs such as if, for, while, loop, fn, return, etc. Check out the grammar for all of the various statement types.

Here's an example showcasing most of them in action:

fn fib(n) {
  if (n <= 1) {
    return n
  }

  return fib(n - 1) + fib(n - 2)
}

for i in range(0, 10) {
  println("fib(" + i + ") = " + fib(i))
}

Expressions

val supports a variety of expressions that can be combined to form more complex operations:

CategoryOperationSyntaxExample
ArithmeticAdditiona + b1 + 2
Subtractiona - b5 - 3
Multiplicationa * b4 * 2
Divisiona / b10 / 2
Moduloa % b7 % 3
Exponentiationa ^ b2 ^ 3
Negation-a-5
LogicalAnda && btrue && false
Ora || btrue || false
Not!a!true
ComparisonEquala == bx == 10
Not Equala != by != 20
Less Thana < ba < b
Less Than or Equala <= bi <= 5
Greater Thana > bcount > 0
Greater Than or Equala >= bvalue >= 100
OtherFunction Callfunction(args)sin(x)
List Indexinglist[index]numbers[0]
List Creation[item1, item2, ...][1, 2, 3]
List Concatenationlist1 + list2[1, 2] + [3, 4]
String Concatenationstring1 + string2"Hello, " + name
Variable Referenceidentifierx

Values

val has several primitive value types:

Number

Numeric values are represented as arbitrary precision floating point numbers (using astro_float under the hood):

> pi
3.141592653589793115997963468544185161590576171875
> e
2.718281828459045090795598298427648842334747314453125
> sin(2) * e ^ pi * cos(sum([1, 2, 3]))
16.4814557939128835908118223753548409318930600432600320575175542910885566534716862696709583557263450637540094805515971245058657340687939442764118452427864231041058959960049996970569867866035825048029794926250103816423751837050040821914044725396611746570949840536443560831710407959633707222226883928822125018007
>

You can specify the rounding mode, and what sort of precision you'd like to see in the output by using the --rounding-mode and --precision options (note that --precision controls binary precision, measured in bits, not decimal digits). respectively.

Boolean

Boolean values represent truth values:

a = true
b = false
c = a && b
d = a || b
e = !a

String

Text values enclosed in single or double quotes:

greeting = "Hello"
name = 'World'
message = greeting + ", " + name + "!"

List

Collections of values of any type:

numbers = [1, 2, 3, 4, 5]
mixed = [1, "two", true, [3, 4]]
empty = []
first = numbers[0]
numbers[0] = 10
combined = numbers + [6, 7]

Function

A function is a value, and can be used in assignments, passed around to other functions, etc.

Check out the higher order functions example for how this works.

fn reduce(l, f, initial) {
  result = initial

  for item in l {
    result = f(result, item)
  }

  return result
}

fn sum(a, b) {
  return a + b
}

l = [1, 2, 3, 4, 5]

println(reduce(l, sum, 0))

Null

Represents the absence of a value.

fn search(l, x) {
  i = 0

  while (i < len(l)) {
    if (l[i] == x) {
      return i
    }

    i = i + 1
  }
}

l = [1, 2, 3, 4, 5]

index = search(l, 6)

if (index == null) {
  println("Value not found")
} else {
  println("Value found at index " + index)
}

Built-ins

val offers a many built-in functions and constants:

CategoryFunction/ConstantDescriptionExample
ConstantspiMathematical constant π (≈3.14159)area = pi * r^2
eMathematical constant e (≈2.71828)growth = e^rate
phiGolden ratio φ (≈1.61803)ratio = phi * width
tauTau constant τ (≈6.28318, 2π)circum = tau * r
Trigonometricsin(x)Sine of x (radians)sin(pi/2)
cos(x)Cosine of x (radians)cos(0)
tan(x)Tangent of x (radians)tan(pi/4)
csc(x)Cosecant of x (radians)csc(pi/6)
sec(x)Secant of x (radians)sec(0)
cot(x)Cotangent of x (radians)cot(pi/4)
Inverse Trigasin(x)Arc sine (-1≤x≤1)asin(0.5)
acos(x)Arc cosine (-1≤x≤1)acos(0.5)
arc(x)Arc tangentarc(1)
acsc(x)Arc cosecant (abs(x)≥1)acsc(2)
asec(x)Arc secant (abs(x)≥1)asec(2)
acot(x)Arc cotangentacot(1)
Hyperbolicsinh(x)Hyperbolic sinesinh(1)
cosh(x)Hyperbolic cosinecosh(1)
tanh(x)Hyperbolic tangenttanh(1)
Logarithmicln(x)Natural logarithmln(e)
log2(x)Base-2 logarithmlog2(8)
log10(x)Base-10 logarithmlog10(100)
e(x)e raised to power xe(2)
Numericsqrt(x)Square root (x≥0)sqrt(16)
ceil(x)Round up to integerceil(4.3)
floor(x)Round down to integerfloor(4.7)
abs(x)Absolute valueabs(-5)
gcd(a, b)Greatest common divisorgcd(12, 8)
lcm(a, b)Least common multiplelcm(4, 6)
Collectionslen(x)Length of list or stringlen("hello")
sum(list)Sum list elementssum([1,2,3])
append(list, val)Add element to end of listappend([1,2], 3)
range(a, b[, s])List from a to b, stepping by srange(0, 10)
Conversionint(x)Convert to integerint("42")
float(x)Convert to floatfloat("3.14")
bool(x)Convert to booleanbool(1)
list(x)Convert to listlist("abc")
I/Oprint(...)Print without newlineprint("Hello")
println(...)Print with newlineprintln("World")
input([prompt])Read line from stdinname = input("Name: ")
Stringsplit(str, delim)Split stringsplit("a,b,c", ",")
join(list, delim)Join list elementsjoin(["a","b"], "-")
Programexit([code])Exit programexit(1)
quit([code])Alias for exitquit(0)

Prior Art

bc(1) - An arbitrary precision calculator language