10 unstable releases (3 breaking)
Uses new Rust 2024
new 0.3.5 | Apr 18, 2025 |
---|---|
0.3.4 | Apr 18, 2025 |
0.2.0 | Apr 17, 2025 |
0.1.1 | Apr 15, 2025 |
0.0.0 | Apr 11, 2025 |
#16 in Science
419 downloads per month
170KB
5K
SLoC
val
val (eval) is a simple arbitrary precision calculator language built on top of chumsky and ariadne.
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
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.5
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> Decimal precision 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]))'
16.481455793912883588
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
, 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)
}
i = 0
while (i < 10) {
println("fib(" + i + ") = " + fib(i))
i = i + 1
}
Expressions
val supports a variety of expressions that can be combined to form more complex operations:
Category | Operation | Syntax | Example |
---|---|---|---|
Arithmetic | Addition | a + b |
1 + 2 |
Subtraction | a - b |
5 - 3 |
|
Multiplication | a * b |
4 * 2 |
|
Division | a / b |
10 / 2 |
|
Modulo | a % b |
7 % 3 |
|
Exponentiation | a ^ b |
2 ^ 3 |
|
Negation | -a |
-5 |
|
Logical | And | a && b |
true && false |
Or | a || b |
true || false |
|
Not | !a |
!true |
|
Comparison | Equal | a == b |
x == 10 |
Not Equal | a != b |
y != 20 |
|
Less Than | a < b |
a < b |
|
Less Than or Equal | a <= b |
i <= 5 |
|
Greater Than | a > b |
count > 0 |
|
Greater Than or Equal | a >= b |
value >= 100 |
|
Other | Function Call | function(args) |
sin(x) |
List Indexing | list[index] |
numbers[0] |
|
List Creation | [item1, item2, ...] |
[1, 2, 3] |
|
List Concatenation | list1 + list2 |
[1, 2] + [3, 4] |
|
String Concatenation | string1 + string2 |
"Hello, " + name |
|
Variable Reference | identifier |
x |
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
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) {
i = 0
result = initial
while (i < len(l)) {
result = f(result, l[i])
i = i + 1
}
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:
Category | Function/Constant | Description | Example |
---|---|---|---|
Constants | pi |
Mathematical constant π (≈3.14159) | area = pi * r^2 |
e |
Mathematical constant e (≈2.71828) | growth = e^rate |
|
phi |
Golden ratio φ (≈1.61803) | ratio = phi * width |
|
tau |
Tau constant τ (≈6.28318, 2π) | circum = tau * r |
|
Trigonometric | sin(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 Trig | asin(x) |
Arc sine (-1≤x≤1) | asin(0.5) |
acos(x) |
Arc cosine (-1≤x≤1) | acos(0.5) |
|
arc(x) |
Arc tangent | arc(1) |
|
acsc(x) |
Arc cosecant (abs(x)≥1) | acsc(2) |
|
asec(x) |
Arc secant (abs(x)≥1) | asec(2) |
|
acot(x) |
Arc cotangent | acot(1) |
|
Hyperbolic | sinh(x) |
Hyperbolic sine | sinh(1) |
cosh(x) |
Hyperbolic cosine | cosh(1) |
|
tanh(x) |
Hyperbolic tangent | tanh(1) |
|
Logarithmic | ln(x) |
Natural logarithm | ln(e) |
log2(x) |
Base-2 logarithm | log2(8) |
|
log10(x) |
Base-10 logarithm | log10(100) |
|
e(x) |
e raised to power x | e(2) |
|
Numeric | sqrt(x) |
Square root (x≥0) | sqrt(16) |
ceil(x) |
Round up to integer | ceil(4.3) |
|
floor(x) |
Round down to integer | floor(4.7) |
|
abs(x) |
Absolute value | abs(-5) |
|
gcd(a, b) |
Greatest common divisor | gcd(12, 8) |
|
lcm(a, b) |
Least common multiple | lcm(4, 6) |
|
Collections | len(x) |
Length of list or string | len("hello") |
sum(list) |
Sum list elements | sum([1,2,3]) |
|
append(list, val) |
Add element to end of list | append([1,2], 3) |
|
Conversion | int(x) |
Convert to integer | int("42") |
float(x) |
Convert to float | float("3.14") |
|
bool(x) |
Convert to boolean | bool(1) |
|
list(x) |
Convert to list | list("abc") |
|
I/O | print(...) |
Print without newline | print("Hello") |
println(...) |
Print with newline | println("World") |
|
input([prompt]) |
Read line from stdin | name = input("Name: ") |
|
String | split(str, delim) |
Split string | split("a,b,c", ",") |
join(list, delim) |
Join list elements | join(["a","b"], "-") |
|
Program | exit([code]) |
Exit program | exit(1) |
quit([code]) |
Alias for exit | quit(0) |
Prior Art
bc(1) - An arbitrary precision calculator language
Dependencies
~13–24MB
~354K SLoC