The epher language reference
Version 0.5.41 (staging). This page is the formal, normative definition
of the epher language: its lexical rules, grammar, types, operators,
statements, built-in constants and functions, units, display rules,
errors, and limits. Every name and every signature here is extracted
from the evaluating engine itself (the epher-core crate), not from
prose.
The user guide teaches the language by example; this reference defines it exactly. Where the two appear to disagree, this page wins, and the disagreement is a bug worth filing.
1. Notation
codespells a token, expression, or statement exactly as written.- italic names in the grammar name non-terminals.
- "number" without qualification means a real value; "complex" includes reals as the special case with zero imaginary part.
- Signatures write built-in calls the way you type them:
hypot(a, b). - Every example in a code block is engine-verified: the
epherblock evaluates, thetextblock is what the engine answers.
2. Program structure
An epher program (a script file, a pasted block, a piped stdin, or a
REPL line) is a sequence of statements separated by ; or by a
newline. Both separators are equivalent; ; never terminates a
statement, it separates statements, so an empty statement is not an
error and leading, trailing, and doubled separators are allowed.
The value of a program is the value of its last statement that produced
one. A statement that produces no value (for example an if without an
else whose condition is false, or break) is skipped when the
program's value is collected; a program whose statements all produce no
value answers nothing.
Comments come in three kinds:
# a line comment, from # to end of line
2 + 2 // another line comment, from // to end of line
/* an inline comment */ 4 * 4
/* a block comment may span
several lines */
4
16
#, //, and /* ... */ may appear between any two tokens. Newlines
inside a block comment do not separate statements; the comment's
closing */ is required, and its absence is a parse error.
A statement is one of: an assignment, a constant definition, a function
definition, a destructuring, if, while, for, solve, return,
break, continue, or an expression. The full grammar is given in
The complete grammar.
3. Lexical grammar
3.1 Numbers
- Decimal: digits with an optional fractional part (
3,3.5,.5) and an optional decimal-exponent suffix (2e3,1.5E-3,2e+2). The exponent needs at least one digit, so2eand2eggstokenize as the number2followed by a name. - Based integers (ADR-0022):
0bbinary,0ooctal,0xhexadecimal,0b101,0o17,0xFF. Digits after the prefix must suit the base. A base prefix changes the spelling, never the value:0xFFis255. - Imaginary suffix (ADR-0043): a number directly followed by
i, with no letter or_after thei, is one imaginary literal:4i,2.5i,0xFFi.3 + 4iis a sum of two literals.4itis not imaginary; it is the number4followed by the nameit, which the grammar then rejects. - All numbers are 64-bit binary floats. Integers are exact up to 2^53; beyond that, spelling a literal keeps at most the 53 most significant bits.
3.2 Strings
A string literal is double quotes around any run of characters. Five
escapes exist (ADR-0064): \n newline, \t tab, \r carriage
return, \\ backslash, \" quote. Any other escape is a parse error
that names the mistake, and a string cannot end on a lone backslash.
"line one\nline two"
"quote: \" backslash: \\"
line one
line two
quote: " backslash: \
3.3 Names
A name starts with a letter or _ and continues with letters, digits,
or _. Letters are Unicode; atan2, log10, and x2 are names.
Names are case-sensitive: G and g are different constants.
4. Names and keywords
The following words are keywords. They cannot be written where a value is expected, and they are recognized by exact spelling:
break const continue def do else end for if
in not or and return solve step
then to while xor
true and false are not literals: booleans are produced only by
comparisons and predicates (1 < 2, isprime(7)), and a bare true
is an unknown-name error.
One further name is reserved: i, the imaginary unit. It cannot be
re-bound by assignment, by const, or by a destructuring pattern; see
Names, scope, and the session store.
i is not a keyword; it resolves as a built-in constant and can be
shadowed by nothing.
5. Types and values
| Type | Written as | Examples |
|---|---|---|
| Float (real number) | 64-bit binary float | 3, 2.5, 1e9 |
| Rational (exact fraction) | produced by frac(n, d), exact(x) |
frac(1, 3) |
| Decimal | produced by dec(x) |
dec(0.1) |
| Big (exact integer/decimal) | produced by big(x), bitwise results |
big(2) ^ 100 |
| Complex | imaginary literals, complex results | 3+4i, sqrt(-1) |
| Boolean | comparisons and predicates only | 1 < 2, isprime(7) |
| String | "..." |
"hello" |
| List | {a, b, c}, a data column of reals, or a list of strings |
{1, 2, 3}, split("a b", " ") |
| Matrix | [[row], [row]], reals, row-major |
[[1, 2], [3, 4]] |
| Quantity | number with a unit suffix | 2 m, 60 mile/hr |
Notes:
- Lists hold numbers, or strings, not a mix. A complex element is rejected at construction.
- Matrices are reals only, stored row-major; matrix functions are in the function index.
- A quantity is an SI value plus its seven base dimensions and an optional display unit; see Units.
ansis a normal variable the frontend rebinds after each computation; it holds the previous answer.
The answer panel spells values as Number display defines.
6. Expressions and operators
6.1 Precedence, tightest to loosest
| Level | Operators | Associativity |
|---|---|---|
| Primary | literals, names, calls, (…), {…}, [[…]] |
, |
| Postfix | ! factorial, % percent, […] index |
left |
| Power | ^ |
right |
| Unary | - negate, ~ bit-not |
, |
| Multiplicative | *, /, unit division 60 mile/hr |
left |
| Additive | +, -, unit conversion in, -> |
left |
| Shift | <<, >> |
left |
| Bit-and | & |
left |
| Bit-or | |, xor |
left |
| Comparison | >, <, >=, <=, ==, != |
non-chaining |
| Not | not |
, |
| And | and |
left |
| Or | or |
left |
| Conditional | if c then a else b (expression form) |
, |
-2 ^ 2 is -4 (unary minus binds looser than the power);
2 ^ -2 is 0.25 (the exponent may be unary);
3! ^ 2 is 36; d[2] ^ 2 indexes first;
not x > 3 is not (x > 3);
5 & 3 == 1 is (5 & 3) == 1.
6.2 Arithmetic
+, -, *, / work over reals and complex; a complex operand
computes in complex. / by zero is a division error. ^ is
right-associative; 0 ^ 0 is 1.
- List arithmetic is elementwise: two same-length lists combine element by element, and a list combined with a scalar maps over the list. Different lengths are a type error.
- String
+concatenates two strings; a string with a non-string is a type error (spell the number first withstrorfixed). - Unit suffixes ride through
+ - * /with dimension checking; see Units.
6.3 Factorial and percent
n! is the factorial of a non-negative whole number. x% is exactly
x / 100, a transparent suffix, so 200 + 10% is 200.1; the
Casio add-on reading (220) is deliberately not a grammar rule.
6.4 Indexing
expr[i] indexes a list or string, 1-based; the index is any
expression. A matrix indexed with one expression gives one whole row
as a list: m[2] is the second row.
6.5 Comparisons
== and != compare any two values of the same kind; <, <=,
>, >= order numbers and strings (strings in dictionary/code-point
order). Complex values refuse ordering. Lists and matrices do not
compare. Comparisons return booleans, and comparisons do not chain:
1 < 2 < 3 is a type error, not (1 < 2) < 3.
6.6 Booleans
and, or, not take booleans only and produce booleans. and and
or short-circuit: the right side is not evaluated when the left
decides. There is no truthiness: numbers are not booleans.
6.7 Bitwise operations
&, |, xor, ~, <<, >> take integers (whole floats, exact
Big, integral rationals/decimals) and produce the exact result masked
to the session's word size, a signed two's-complement word of 8, 16,
32, or 64 bits, set with bits(w) and defaulting to 64. Right shift
is arithmetic; a negative shift amount reverses the direction.
7. Statements
7.1 Assignment: name = expr
Evaluates the expression and binds the name in the session. Rebinding
is allowed; binding i is refused (see below).
7.2 Constant definition: const name = expr
Binds an immutable name. Re-declaring with the same value succeeds,
examples get pasted twice; a different value is a
constant already defined error. Naming an existing variable is a
cannot define constant x: the name is already a variable error.
7.3 Destructuring: {a, b} = expr (ADR-0064)
The expression must be a list whose length equals the number of
pattern names; each name binds to its position's element. _ skips a
position: {x, _} = {1, 2} binds only x. Names in the pattern
follow assignment rules (i refused, constants refused). The pattern
is recognized only when the statement begins with {names} =; a
{1, 2} at the start of a line stays the list expression it always
was.
7.4 Function definition: def name(params) = expr and def name(params) do … end
Two body forms. = expr evaluates the expression in a child
environment seeded with the arguments. do … end runs its statements
one after another in that child environment; the last statement's
value is the call's answer, and a body whose statements produce no
value is a named error. Recursion works in both forms. A call carries
its own 100,000-step budget. Parameters shadow session values only
inside the call.
7.5 return, break, continue (ADR-0064)
return expr leaves the enclosing function now, with the value;
return at top level (outside any function) is a named error.
break leaves the innermost enclosing for or while; continue
skips to its next pass. Outside a loop both are named errors. In a
for loop, a pass ended by continue contributes no value to the
collected list.
7.6 if as a statement: if cond then stmt / if cond then stmt else stmt
The condition must be a boolean. Without else, a false condition
produces no value, which is how a for loop filters: a pass whose
body produces no value adds nothing to the loop's list.
7.7 while cond do stmt
Evaluates the condition (which must be a boolean) and runs the
one-statement body until it is false, break fires, or the step
budget runs out. A while loop produces no value.
7.8 for name in iterable do stmt (ADR-0054, scoped per ADR-0063)
Two iterable forms: a range start to end (optionally step s), or
any expression evaluating to a list. The variable is bound per pass
and removed afterwards: the loop never leaves its variable in the
session, and a binding that existed before the loop is restored after
it. The loop's value is the list of the body's per-pass values.
7.9 solve lhs == rhs (ADR-0043)
Numeric equation solving, no CAS. The equation must use ==. The
unknown is x when x appears, otherwise the single other name;
constants (built-in and user) are parameters, never unknowns. Real
roots in a wide search window are reported, as a display string:
solve x^2 == 9 answers x = -3, x = 3.
7.10 Expression statements
Any expression, typically a call. Its value, when there is one, becomes the statement's value.
8. Names, scope, and the session store
Name resolution tries, in order: session variables, then session
constants, then built-in constants, then built-in functions (a call),
then the error unknown name. A user function or constant shadows a
built-in of the same name.
- Session variables persist for the session and, in interactive
frontends, across sessions and frontends of the same installation
through the shared store (the
.epherfolder; the PWA keeps the same shape in browser storage). - The imaginary unit is reserved.
i = 5,const i = …, and a destructuring pattern namingiare all refused withcannot assign to i: that name is the imaginary unit, so no session, store, or frontend can shadow3+4i. A storedifrom an older release is dropped silently when the store loads. - A
forloop's variable exists only for the loop: it is removed afterwards, and a prior binding is restored (nothing a loop does reaches the store). - A
def's parameters and body live in a child environment: they shadow the session inside the call and cannot leak out. Assignments inside a function body are local to that call. save nameasks the interactive frontend to persist a function or constant into the store; saving a script is a frontend command, not language (see Beyond the grammar).
9. Built-in constants
Values are SI throughout. A name's value is fixed by the engine; a
user const of the same name shadows it by the resolution order.
9.1 Mathematics
| Name | Value | Meaning |
|---|---|---|
pi |
3.141592653589793 |
the circle constant |
tau |
6.283185307179586 |
two pi |
e |
2.718281828459045 |
Euler's number, the natural-log base |
phi |
1.618033988749895 |
the golden ratio, (1+√5)/2 |
gamma |
0.5772156649015329 |
the Euler–Mascheroni constant |
i |
0+1i (the imaginary unit) |
the imaginary unit: the one reserved name (assignment refuses it) |
9.2 Astronomy
| Name | Value | Meaning |
|---|---|---|
au |
1.495978707e11 |
astronomical unit (m) |
pc |
3.0856775814913673e16 |
parsec (m) |
ly |
9.4607304725808e15 |
light year (m) |
c |
2.99792458e8 |
speed of light in vacuum (m/s) |
g |
9.80665 |
standard gravity (m/s²) |
l_sun |
3.828e26 |
solar luminosity (W) |
m_sun |
1.98847e30 |
solar mass (kg) |
r_sun |
6.957e8 |
solar radius (m) |
m_earth |
5.9722e24 |
Earth mass (kg) |
r_earth |
6.371e6 |
Earth mean radius (m) |
m_moon |
7.342e22 |
lunar mass (kg) |
r_moon |
1.7374e6 |
lunar mean radius (m) |
h |
6.62607015e-34 |
Planck constant (J·s) |
h_bar |
h/tau = 1.0545718176461565e-34 |
reduced Planck constant (J·s) |
k_b |
1.380649e-23 |
Boltzmann constant (J/K) |
9.3 Physics
| Name | Value | Meaning |
|---|---|---|
G |
6.6743e-11 |
gravitational constant (m³/(kg·s²)) |
m_e |
9.1093837139e-31 |
electron mass (kg) |
m_p |
1.67262192595e-27 |
proton mass (kg) |
m_n |
1.67492750056e-27 |
neutron mass (kg) |
m_u |
1.66053906892e-27 |
atomic mass unit (kg) |
q_e |
1.602176634e-19 |
elementary charge (C) |
ev |
1.602176634e-19 |
electronvolt (J) |
eps_0 |
8.8541878128e-12 |
vacuum permittivity (F/m) |
mu_0 |
1.25663706212e-6 |
vacuum permeability (H/m) |
z_0 |
376.730313668 |
vacuum impedance (Ω) |
mu_b |
9.2740100783e-24 |
Bohr magneton (J/T) |
mu_n |
5.050783699e-27 |
nuclear magneton (J/T) |
phi_0 |
2.067833848e-15 |
magnetic flux quantum (Wb) |
a_0 |
5.29177210544e-11 |
Bohr radius (m) |
alpha |
7.2973525643e-3 |
fine-structure constant |
r_inf |
10973731.56816 |
Rydberg constant (1/m) |
r_e |
2.8179403205e-15 |
classical electron radius (m) |
lambda_c |
2.42631023867e-12 |
electron Compton wavelength (m) |
l_P |
1.616255e-35 |
Planck length (m) |
m_P |
2.176434e-8 |
Planck mass (kg) |
t_P |
5.391247e-44 |
Planck time (s) |
9.4 Chemistry and thermodynamics
| Name | Value | Meaning |
|---|---|---|
atm |
101325.0 |
standard atmosphere (Pa) |
n_a |
6.02214076e23 |
Avogadro constant (1/mol) |
faraday |
96485.33212 |
Faraday constant (C/mol) |
r_gas |
8.31446261815324 |
molar gas constant (J/(mol·K)) |
sigma_sb |
5.670374419e-8 |
Stefan–Boltzmann constant (W/(m²·K⁴)) |
wien |
2.897771955e-3 |
Wien displacement constant (m·K) |
The imaginary unit's value is the complex number with real part 0 and
imaginary part 1; its literal spellings i and 4i tokenize as
literals.
10. Built-in functions
Every callable the engine knows, grouped by domain. Argument shapes are
normative: x is a real number, z any number (complex allowed),
n a whole number, s a string, L a list. A call with the wrong
count or kinds is a type error that names the function, e.g.
rad expects 1 number, got 1 argument(s).
10.1 Angles and logarithms
| Function | Answers |
|---|---|
deg(x) |
x radians to degrees |
rad(x) |
x degrees to radians |
ln(z) |
natural logarithm; ln(-1) is i*pi |
log(z) |
base-10 logarithm (calculator convention) |
log2(z) |
base-2 logarithm |
logb(base, x) |
logarithm of x to the given base |
exp(z) |
e to the x |
10.2 Powers, roots, rounding
| Function | Answers |
|---|---|
sqrt(q) |
square root; negative reals fall back to complex; quantities need even dimensions |
cbrt(z) |
real cube root (principal complex root for complex) |
root(n, x) |
real n-th root; odd roots of negatives are negative |
abs(z) |
magnitude: for complex, distance from the origin |
floor(x), ceil(x), trunc(x) |
round down, up, toward zero |
round(x) |
half away from zero, like a calculator |
sign(x) |
-1, 0, or 1 |
hypot(a, b) |
sqrt(a^2 + b^2) without the overflow |
min(x…), max(x…) |
extreme of any number of reals |
gcd(a, b), lcm(a, b) |
whole-number gcd and lcm |
10.3 Trigonometry (complex where the domain needs it)
| Function | Answers |
|---|---|
sin(z), cos(z), tan(z) |
circular functions |
asin(z), acos(z), atan(z) |
inverse circular functions; real asin(2) falls back to complex |
atan2(y, x) |
angle of the point (x, y), quadrant-correct |
sinh(z), cosh(z), tanh(z) |
hyperbolic functions |
asinh(z), acosh(z), atanh(z) |
inverse hyperbolic functions |
10.4 Complex parts
| Function | Answers |
|---|---|
re(z), im(z) |
real and imaginary part (0 for a real) |
arg(z) |
principal argument |
conj(z) |
complex conjugate (a real passes through) |
10.5 Exact and display spellings
| Function | Answers |
|---|---|
exact(x) |
the rational behind a float, when one agrees through all twelve displayed digits (exact(0.3333333333333333) is 1/3); irrationals pass through |
frac(n, d) |
the exact fraction n/d |
bin(n), oct(n), hex(n) |
spelling with prefix: bin(10) is 0b1010; negatives keep the sign on the prefix |
dec(x) |
the value as an exact decimal |
big(x) |
the value as an exact (big) integer |
scientific(x) |
the value in scientific notation, as text |
engineering(x) |
the value in engineering notation (exponent a multiple of 3), as text |
grouped(x) |
the value with thin-space thousands grouping, as text |
fixed(x, d) |
the value with exactly d decimals (0–15), as text |
str(v) |
one value spelled the way the answer panel spells it |
print(v…) |
the arguments joined with spaces, as one string |
10.6 Number theory
Integers the float type reaches exactly (|n| < 2^53).
| Function | Answers |
|---|---|
fact(n) |
n! |
ncr(n, r), npr(n, r) |
combinations and permutations |
isprime(n) |
whether n is prime (deterministic Miller–Rabin) |
nextprime(n), prevprime(n) |
nearest prime above/below |
modpow(b, e, m) |
b to the e modulo m, exact via big integers |
mod(a, b) |
truncated remainder: the sign of the dividend |
totient(n) |
Euler's totient |
ndivisors(n) |
how many whole numbers divide n |
factors(n) |
the prime factorization, as text: factors(360) is 2^3 * 3^2 * 5 |
10.7 Statistics and probability
L is a list of reals; variance is population variance (divide by n).
| Function | Answers |
|---|---|
sum(L), product(L) |
total and product (lists or bare numbers) |
mean(L), median(L), mode(L) |
centers |
variance(L), stdev(L) |
spread |
range(L) |
max minus min |
quartile(L, k) |
quartile k in 1..3 |
sort(L) |
the list, ascending |
linreg(xs, ys) |
least-squares line and r, as text |
quadreg(xs, ys) |
quadratic fit and r, as text |
expreg(xs, ys) |
exponential fit and r, as text |
powreg(xs, ys) |
power fit and r, as text |
logreg(xs, ys) |
logarithmic fit and r, as text |
normpdf(x), normcdf(x), invnorm(p) |
standard normal (1 argument) or general (x, mu, sigma) |
tpdf(x, df), tcdf(x, df), invt(p, df) |
Student's t |
chi2pdf(x, df), chi2cdf(x, df), invchi2(p, df) |
chi-square |
binompdf(k, n, p), binomcdf(k, n, p) |
binomial |
poissonpdf(k, lambda), poissoncdf(k, lambda) |
Poisson |
ztest(L, mu0, sigma) |
one-sample z test, z = …, p = … |
ttest(L, mu0) |
one-sample t test |
ttestpaired(A, B) |
paired t on the differences |
anova(G1, G2, …) |
one-way ANOVA, F = …, p = … |
zinterval(L, sigma, level) |
confidence interval, as text |
tinterval(L, level) |
confidence interval, as text |
chisq_gof(observed, expected) |
chi-square goodness of fit |
10.8 Lists, strings, matrices
| Function | Answers |
|---|---|
len(v) |
length of a list, or character count of a string |
upper(s), lower(s), trim(s) |
case and whitespace |
substr(s, start[, len]) |
1-based slice; no len means the rest |
split(s, sep) |
the parts, as a list of strings (non-empty sep) |
join(L, sep) |
elements spelled as print would, joined |
find(s, sub) |
1-based position of sub, or 0 |
replace(s, old, new) |
every old replaced |
det(M), trace(M) |
square-matrix determinant and trace |
inv(M) |
square-matrix inverse |
transpose(M) |
rows and columns swapped |
dim(M) |
{rows, cols} |
ref(M), rref(M) |
row echelon and reduced row echelon form |
10.9 Calculus (numeric)
| Function | Answers |
|---|---|
derivative(expr, at) |
5-point central difference of expr at at; expr stays symbolic, and the unknown is x when x appears, else the single free name |
integral(expr, a, b) |
definite integral of expr from a to b |
10.10 Randomness (seeded, reproducible)
| Function | Answers |
|---|---|
random() |
uniform draw in [0, 1) |
random(a, b) |
uniform draw in [a, b) |
randint(a, b) |
whole number in the closed range [a, b] |
randn() |
standard normal draw (Box–Muller) |
randseed(n) |
re-seed the generator and report n; a fresh session seeds from the clock, randseed makes draws reproducible |
10.11 Word size
| Function | Answers |
|---|---|
bits() |
the current bitwise word size (8, 16, 32, or 64) |
bits(w) |
set the word size to w and report it |
10.12 Finance
Sign convention: money you pay out is negative. r is the rate per
period.
| Function | Answers |
|---|---|
tvm_n(r, pv, pmt, fv) |
periods needed |
tvm_i(n, pv, pmt, fv) |
periodic rate that makes the amounts balance |
tvm_pv(n, r, pmt, fv) |
present value |
tvm_pmt(n, r, pv, fv) |
payment per period |
tvm_fv(n, r, pv, pmt) |
future value |
npv(r, flows) |
net present value of a flow list |
irr(flows) |
internal rate of return of a flow list |
amort(p, r, n, k) |
balance after period k of an n-period loan |
simple_interest(p, r, t) |
p*r*t |
compound_interest(p, r, n) |
interest earned: p(1+r)^n − p |
10.13 Astronomy and time
Bodies are whole numbers: Mercury 1 … Neptune 8, Pluto 9, Sun 10,
Moon 11. jd is a Julian Date. Observer functions take terrestrial
latitude and longitude in degrees.
| Function | Answers |
|---|---|
jd(y, m, d[, hr]) |
Julian Date of a calendar date (Gregorian from 1582-10-15, Julian before) |
mjd(y, m, d[, hr]) |
Modified Julian Date |
date(jd) |
{y, m, d} |
time(jd) |
{h, min, s} |
iso(jd) |
the instant as YYYY-MM-DDTHH:MM:SS text |
now() |
the current Julian Date |
hms2deg(h, m, s), dms2deg(d, m, s) |
sexagesimal to decimal degrees |
deg2hms(x), deg2dms(x) |
decimal degrees to sexagesimal text |
lst(jd, lon) |
local sidereal time in hours at longitude lon |
delta_t(jd) |
TT − UT1 in seconds |
kepler(M, e) |
Kepler's equation: eccentric anomaly for mean anomaly M |
ra(body, jd), decl(body, jd) |
geocentric equatorial coordinates, degrees |
dist(body, jd) |
distance, km |
mag(body, jd) |
apparent magnitude |
phase(body, jd) |
phase angle, degrees |
illum(body, jd) |
illuminated fraction |
diam(body, jd) |
angular diameter, arcseconds |
alt(body, jd, lat, lon), az(body, jd, lat, lon) |
topocentric horizon coordinates, degrees |
airmass(body, jd, lat, lon) |
relative airmass along the line of sight |
rise(body, jd, lat, lon), set(…), transit(…) |
the event's JD for the local day containing jd; a named error when the event never happens that day |
dawes(aperture) |
Dawes limit in arcseconds for an aperture in mm |
dist_mod(mu) |
distance modulus mu to distance in parsecs |
mag2jy(m), jy2mag(f) |
Jansky ↔ magnitude (AB, 3631 Jy zero point) |
satx(moon, jd), saty(…), satz(…) |
a Jupiter satellite's offset from Jupiter, in Jupiter radii (moon 1..4 = Io..Callisto) |
satsep(m1, m2, jd) |
separation of two satellites, in Jupiter radii |
satphen(moon, jd) |
the satellite's next phenomenon (occultation, transit, eclipse, shadow), as text |
march_equinox(y), june_solstice(y), september_equinox(y), december_solstice(y) |
the JD of the Earth's season mark in year y |
i the loop variable and i the imaginary unit coexist inside a
for loop exactly because the loop scopes its variable; see
Statements.
11. Units
A number directly followed by a unit name is a quantity (ADR-0046):
the number times the unit's SI factor, carrying its dimensions.
2 m stores 2 with length dimensions; 60 mile/hr stores
26.8224 m/s. Units may carry a whole-number power (2 m^2, powers to
±127) and a / may continue with another unit (5 m/s^2), that is
unit syntax, not division, whenever a unit name follows. An SI prefix
may start a unit: 30 cm, 5 kHz, 3 ns.
x in unit (or x -> unit) converts: 5 km in mile answers
3.10685596119 mile, and the display unit sticks. Arithmetic checks
dimensions: adding 2 m to 30 cm gives 2.3 m; adding a metre to
a second is a dimension error.
11.1 Prefixes
The 21 SI prefixes, as spellings before a unit: da (×10¹), h (×10²),
k (×10³), M (×10⁶), G (×10⁹), T (×10¹²), P (×10¹⁵),
E (×10¹⁸), Z (×10²¹), Y (×10²⁴), d (×10⁻¹), c (×10⁻²),
m (×10⁻³), µ or u (×10⁻⁶), n (×10⁻⁹), p (×10⁻¹²),
f (×10⁻¹⁵), a (×10⁻¹⁸), z (×10⁻²¹), y (×10⁻²⁴).
11.2 Base and derived units
| Unit | SI factor | Dimensions |
|---|---|---|
m |
1 | length |
s |
1 | time |
g |
1e-3 | mass |
kg |
1 | mass |
A |
1 | current |
K |
1 | temperature |
mol |
1 | amount |
cd |
1 | luminous intensity |
Hz |
1 | 1/time |
N |
1 | kg·m/s² |
Pa |
1 | kg/(m·s²) |
J |
1 | kg·m²/s² |
W |
1 | kg·m²/s³ |
C |
1 | A·s |
V |
1 | kg·m²/(A·s³) |
F |
1 | A²·s⁴/(kg·m²) |
ohm, Ohm |
1 | kg·m²/(A²·s³) |
S |
1 | A²·s³/(kg·m²) |
Wb |
1 | kg·m²/(A·s²) |
T |
1 | kg/(A·s²) |
H |
1 | kg·m²/(A²·s²) |
lm |
1 | cd·sr (cd) |
lx |
1 | cd/m² |
Bq |
1 | 1/s |
Gy |
1 | m²/s² |
Sv |
1 | m²/s² |
L, l |
1e-3 | m³ |
t |
1e3 | mass |
bar |
1e5 | kg/(m·s²) |
atm |
101325 | kg/(m·s²) |
torr |
133.32236842105263 | kg/(m·s²) |
psi |
6894.757293168361 | kg/(m·s²) |
eV |
1.602176634e-19 | kg·m²/s² |
min |
60 | time |
hr |
3600 | time |
d |
86400 | time |
yr |
31557600 | time |
rad |
1 | angle (dimensionless) |
deg |
pi/180 | angle (dimensionless) |
arcmin |
pi/10800 | angle (dimensionless) |
arcsec |
pi/648000 | angle (dimensionless) |
mile |
1609.344 | length |
yd |
0.9144 | length |
ft |
0.3048 | length |
inch |
0.0254 | length |
nmi |
1852 | length |
lb |
0.45359237 | mass |
oz |
0.028349523125 | mass |
gal |
3.785411784e-3 | m³ |
qt |
9.46352946e-4 | m³ |
pt |
4.73176473e-4 | m³ |
mph |
0.44704 | m/s |
knot |
0.5144444444444445 | m/s |
AU, au |
1.495978707e11 | length |
pc |
3.0856775814913673e16 | length |
ly |
9.4607304725808e15 | length |
Jy |
1e-26 | kg/s² |
A quantity displays in its display unit when it has one (typed or
converted), otherwise in the SI spelling of its dimensions (m/s^2,
or the exact derived name when the dimensions match one: N, W,
Pa, …).
12. Number display
The answer panel's automatic spelling (ADR-0051):
- A float prints with up to 12 significant digits, exact integers keeping every digit.
- Exact fractions: when a rational with denominator ≤ 1000 agrees
with the value through all twelve displayed digits and is not a
terminating decimal, the answer shows as a fraction,
1/3stays1/3, while0.1 + 0.2shows0.3, not3/10. - Complex answers print in
a+biform. Booleans printtrue/false. Lists print{1, 2, 3}; matrices print row by row:[[1, 2], [3, 4]]. - The display verbs (
scientific,engineering,grouped,fixed,bin,oct,hex) return text, ready toprintorjoin.
13. Errors
An error stops evaluation of the statement in progress; in a script the whole run stops and the exit carries the message. Error classes, with the verbatim shape of each message:
| Class | Example message |
|---|---|
| Parse | unexpected character: '@'; expected digits after 0x; unknown escape \d in a string: the escapes are \n, \t, \r and \\; unterminated string: a literal needs its closing quote; unterminated block comment: expected */ |
| Type | rad expects 1 number, got 1 argument(s); if condition must be a boolean, got 3; and expects booleans, got 2; lists have different lengths: 3 and 1; cannot compare Complex(Complex { re: 3.0, im: 4.0 }) |
| Domain | sqrt of negative number -4 (as a real); asin of 2 outside -1..1; no prime below 2; factors of 0 must be a positive integer |
| Division | division by zero |
| Name | unknown name: nmae |
| Constant/variable conflicts | cannot assign to constant TAX; cannot define constant x: the name is already a variable |
| Reserved name | cannot assign to i: that name is the imaginary unit |
| Control flow | break outside a loop; continue outside a loop; return outside a function |
| Limits | the step budget exhausted; for runs at most 100000 iterations, got 100001 |
| Dimension | cannot add m and s (dimension mismatch); cannot take the square root of 2 m: the dimensions do not divide evenly |
14. Limits and determinism
- Step budget: every program run carries a 100,000-step budget; each function call gets its own fresh budget. Exhausting it stops the run.
- For iterations: a single
forloop is capped at 100,000 passes. - Floats: 64-bit binary; integers exact to 2^53.
- Reproducibility: the random generator is SplitMix64 with a
Box–Muller step for
randn;randseed(n)makes a sequence repeatable. Scripts evaluated by the checker pin a seed, so a script's transcript is byte-stable. - Display: 12 significant digits, fractions only through denominator 1000.
15. The complete grammar (EBNF)
The grammar is written in EBNF, the Extended Backus–Naur Form. Each rule
names a construct and says how it is built: , joins parts in sequence,
| separates alternatives, [ … ] marks a part that may be absent,
{ … } a part that may repeat, and "quoted text" is a token written
exactly so; rules refer to one another until every chain ends in a
literal token, and any input that a chain of these rules accepts parses,
and anything else does not.
program = statement , { separator , statement } ;
separator = ";" | newline ;
statement = "break"
| "continue"
| "return" , expression
| "if" , expression , "then" , statement , [ "else" , statement ]
| "while" , expression , "do" , statement
| "for" , name , "in" , expression , [ "to" , expression ,
[ "step" , expression ] ] , "do" , statement
| "solve" , expression
| "const" , name , "=" , expression
| "def" , name , "(" , [ params ] , ")" , defbody
| pattern , "=" , expression
| name , "=" , expression
| expression ;
params = name , { "," , name } ;
defbody = "=" , expression
| "do" , block , "end" ;
pattern = "{" , name , { "," , name } , "}" ;
block = statement , { separator , statement } ;
expression = ifexpr ;
ifexpr = "if" , expression , "then" , expression ,
"else" , expression | orexpr ;
orexpr = andexpr , { "or" , andexpr } ;
andexpr = notexpr , { "and" , notexpr } ;
notexpr = "not" , notexpr | comparison ;
comparison = bitor , [ cmpop , bitor ] ;
cmpop = ">" | "<" | ">=" | "<=" | "==" | "!=" ;
bitor = bitand , { ( "|" | "xor" ) , bitand } ;
bitand = shift , { "&" , shift } ;
shift = additive , [ ( "<<" | ">>" ) , additive ] ;
additive = term , { ( "+" | "-" ) , term } ;
term = unary , { ( "*" | "/" ) , unary } ;
unary = "-" , unary | "~" , unary | power ;
power = postfix , [ "^" , unary ] ;
postfix = primary , { "!" | "%" | index } ;
index = "[" , expression , "]" ;
primary = number | string | name | call
| "(" , expression , ")"
| list | matrix ;
call = name , "(" , [ expression , { "," , expression } ] , ")" ;
list = "{" , [ expression , { "," , expression } ] , "}" ;
matrix = "[" , row , { "," , row } , "]" ;
row = "[" , expression , { "," , expression } , "]" ;
Grammar notes the EBNF cannot say:
- The additive level also admits unit conversion after a term:
expression "in" unitpathandexpression "->" unitpath, looping so3 m in ft in inchchains. - Inside a multiplicative chain, a
/directly followed by a unit name (and not a call) continues a unit path rather than dividing:60 mile/hr,5 m/s^2. - A statement beginning
{ name , … } =is a destructuring; any other{begins a list expression (the parser rewinds). endcloses adef'sdoblock; there is no standalonedostatement.inis a keyword both infor … inand in unit conversion; the position disambiguates.- Number literals absorb an
isuffix into an imaginary token; based literals (0b,0o,0x) too.
16. Beyond the grammar: interactive commands
The language is what this page defines. The frontends add commands around it, recognized before the parser sees the line; they are conveniences, not part of the language definition:
| Command | Where | Meaning |
|---|---|---|
save name |
interactive frontends | persist a function or constant to the store |
save script name |
interactive frontends | persist the session as a named script |
graph expr |
REPL, TUI, PWA, desktop | draw a 2D plot of expr over the session's window |
graph3d expr |
REPL, TUI, PWA, desktop | draw a 3D surface |
language |
interactive frontends | switch the interface language |
quit, exit |
REPL | end the session |
Everything else, expression entry, scripts, the store, behaves as this reference defines, identically on every frontend.
This reference corresponds to epher 0.5.41 (staging). The function
and constant tables are extracted from epher-core's dispatch and
catalog; a core test keeps them from drifting: renaming or removing a
builtin without updating this page fails the build.