@thi.ng/units
Version:
Extensible SI unit creation, conversions, quantities & calculations (incl. List-like DSL and ~170 predefined units & constants)
59 lines (58 loc) • 1.71 kB
JavaScript
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
import { parse } from "@thi.ng/sexpr/parse";
import { runtime } from "@thi.ng/sexpr/runtime";
import * as CONST from "./constants/index.js";
import {
add,
asUnit,
convert,
div,
mul,
quantity,
Quantity,
reciprocal,
sub
} from "./unit.js";
const __mathOp = (fn, fn1) => (first, ...args) => {
return args.length > 0 ? (
// use a reduction for 2+ args
args.reduce((acc, x) => fn(acc, x), first)
) : (
// apply special case unary function
fn1(first)
);
};
const ENV = {
"+": __mathOp(add, (x) => x),
"-": __mathOp(sub, (x) => sub(quantity(0, x.value), x)),
"*": __mathOp(mul, (x) => x),
"/": __mathOp(div, reciprocal),
area: (src) => {
const [w, h] = src.value.map((x) => quantity(1, x));
return mul(w, h);
},
volume: (src) => {
const [w, h, d] = src.value.map((x) => quantity(1, x));
return mul(mul(w, h), d);
},
width: (src) => quantity(1, src.value[0]),
height: (src) => quantity(1, src.value[1]),
depth: (src) => quantity(1, src.value[2])
};
const interpret = runtime({
expr: ({ children: [x, ...args] }) => {
const name = x.value;
const $args = args.map((a) => interpret(a, null));
return ENV[name]?.apply(null, $args) ?? convert($args[0], asUnit(name));
},
sym: ({ value }) => {
const match = /^[-+]?[0-9.]+(e[+-]?\d+)?/.exec(value);
return match ? quantity(+match[0], asUnit(value.substring(match[0].length))) : CONST[value.toUpperCase()] ?? asUnit(value);
},
str: () => illegalArgs("string value"),
num: (x) => x.value
});
const $eval = (src) => parse(src).children.reduce((_, x) => interpret(x, null), null);
export {
$eval
};