@thi.ng/units 1.0.33 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Mastodon Follow](https://img.shields.io/mastodon/follow/109331703950160316?domain=https%3A%2F%2Fmastodon.thi.ng&style=social)](https://mastodon.thi.ng/@toxi)
8
8
 
9
9
  > [!NOTE]
10
- > This is one of 210 standalone projects, maintained as part
10
+ > This is one of 212 standalone projects, maintained as part
11
11
  > of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo
12
12
  > and anti-framework.
13
13
  >
@@ -43,6 +43,7 @@
43
43
  - [Unit conversions](#unit-conversions)
44
44
  - [Quantities](#quantities)
45
45
  - [Constants](#constants)
46
+ - [Domain-specific language](#domain-specific-language)
46
47
  - [Status](#status)
47
48
  - [Installation](#installation)
48
49
  - [Dependencies](#dependencies)
@@ -52,7 +53,7 @@
52
53
 
53
54
  ## About
54
55
 
55
- Extensible SI unit creation, conversions, quantities & calculations (incl. ~170 predefined units & constants).
56
+ Extensible SI unit creation, conversions, quantities & calculations (incl. List-like DSL and ~170 predefined units & constants).
56
57
 
57
58
  All unit definitions, quantities & conversions are based on the SI unit system &
58
59
  concepts described here:
@@ -618,6 +619,42 @@ Densities of selected materials:
618
619
  | `WATER` | `kg/m3` |
619
620
  | `WOOD` | `kg/m3` |
620
621
 
622
+ ## Domain-specific language
623
+
624
+ The package includes a minimal Lisp-like formula expression language to compute,
625
+ combine or convert quantities in a more concise style than the default JS API
626
+ provided by this package.
627
+
628
+ The minimal domain specific language defined here only includes the following
629
+ functions (all following basic Lisp syntax, also see examples below):
630
+
631
+ - Math operators: `+`, `-`, `*`, `/` to combine two or more quantities (all quantities MUST be compatible)
632
+ - `area`: Computes the area of a given 2D quantity (e.g. `(area DIN_A4)` computes the area of a sheet of paper)
633
+ - `volume`: Computes the volume of a given 3D quantity
634
+ - `width`: Returns the 1st dimension quantity of a 2D/3D quantity (e.g. `(width DIN_A4)` = 210mm)
635
+ - `height`: Returns the 2nd dimension quantity of a 2D/3D quantity
636
+ - `depth`: Returns the 3rd dimension quantity of a 3D quantity
637
+
638
+ Any other symbol is interpreted as quantity or pre-defined registered unit or
639
+ constant (see readme). Quantities always have to be given without whitespace,
640
+ i.e. `100mm` vs. `100 mm`.
641
+
642
+ ```ts
643
+ import { $eval } from "@thi.ng/units";
644
+
645
+ // compute weight in grams of A4 paper with 320 grams per square meter
646
+ console.log($eval(`(g (* (area din_a4) 320gsm))`));
647
+ // 19.9584
648
+
649
+ // compute weight in kg of 1/2 inch thick 200x300mm glass plate
650
+ console.log($eval(`(kg (* 200mm 300mm 0.5in 2500kg/m3))`));
651
+ // 1.905
652
+
653
+ // same as previous but using the `glass` density preset
654
+ console.log($eval(`(kg (* 200mm 300mm 0.5in glass))`));
655
+ // 1.905
656
+ ```
657
+
621
658
  ## Status
622
659
 
623
660
  **BETA** - possibly breaking changes forthcoming
@@ -650,7 +687,7 @@ For Node.js REPL:
650
687
  const units = await import("@thi.ng/units");
651
688
  ```
652
689
 
653
- Package sizes (brotli'd, pre-treeshake): ESM: 4.79 KB
690
+ Package sizes (brotli'd, pre-treeshake): ESM: 5.65 KB
654
691
 
655
692
  ## Dependencies
656
693
 
@@ -658,6 +695,7 @@ Package sizes (brotli'd, pre-treeshake): ESM: 4.79 KB
658
695
  - [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks)
659
696
  - [@thi.ng/equiv](https://github.com/thi-ng/umbrella/tree/develop/packages/equiv)
660
697
  - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/develop/packages/errors)
698
+ - [@thi.ng/sexpr](https://github.com/thi-ng/umbrella/tree/develop/packages/sexpr)
661
699
 
662
700
  Note: @thi.ng/api is in _most_ cases a type-only import (not used at runtime)
663
701
 
@@ -0,0 +1,5 @@
1
+ export * from "./densities.js";
2
+ export * from "./earth.js";
3
+ export * from "./paper-sizes.js";
4
+ export * from "./velocities.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,4 @@
1
+ export * from "./densities.js";
2
+ export * from "./earth.js";
3
+ export * from "./paper-sizes.js";
4
+ export * from "./velocities.js";
package/dsl.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Parses & evaluates given Lisp-like formula expression to compute, combine or
3
+ * convert units in a more concise style than the default JS API provided by
4
+ * this package.
5
+ *
6
+ * @remarks
7
+ * The minimal domain specific language defined here only includes the following
8
+ * functions (all following basic Lisp syntax, also see examples below):
9
+ *
10
+ * - Math operators: `+`, `-`, `*`, `/` to combine two or more quantities (all
11
+ * quantities MUST be compatible)
12
+ * - `area`: Computes the area of a given 2D quantity (e.g. `(area DIN_A4)`
13
+ * computes the area of a sheet of paper)
14
+ * - `volume`: Computes the volume of a given 3D quantity
15
+ * - `width`: Returns the 1st dimension quantity of a 2D/3D quantity (e.g.
16
+ * `(width DIN_A4)` = 210mm)
17
+ * - `height`: Returns the 2nd dimension quantity of a 2D/3D quantity
18
+ * - `depth`: Returns the 3rd dimension quantity of a 3D quantity
19
+ *
20
+ * Any other symbol is interpreted as quantity or pre-defined registered unit or
21
+ * constant (see readme).
22
+ *
23
+ * @example
24
+ * ```ts tangle:../export/eval.ts
25
+ * import { $eval } from "@thi.ng/units";
26
+ *
27
+ * // compute weight in grams of A4 paper with 320 grams per square meter
28
+ * console.log($eval(`(g (* (area DIN_A4) 320gsm))`));
29
+ * // 19.9584
30
+ *
31
+ * // compute weight in kg of 1/2 inch thick 200x300mm glass plate
32
+ * console.log($eval(`(kg (* 200mm 300mm 0.5in 2500kg/m3))`));
33
+ * // 1.905
34
+ *
35
+ * // same as previous but using the `glass` density preset
36
+ * console.log($eval(`(kg (* 200mm 300mm 0.5in glass))`));
37
+ * // 1.905
38
+ * ```
39
+ *
40
+ * @param src
41
+ */
42
+ export declare const $eval: (src: string) => any;
43
+ //# sourceMappingURL=dsl.d.ts.map
package/dsl.js ADDED
@@ -0,0 +1,58 @@
1
+ import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
2
+ import { parse } from "@thi.ng/sexpr/parse";
3
+ import { runtime } from "@thi.ng/sexpr/runtime";
4
+ import * as CONST from "./constants/index.js";
5
+ import {
6
+ add,
7
+ asUnit,
8
+ convert,
9
+ div,
10
+ mul,
11
+ quantity,
12
+ Quantity,
13
+ reciprocal,
14
+ sub
15
+ } from "./unit.js";
16
+ const __mathOp = (fn, fn1) => (first, ...args) => {
17
+ return args.length > 0 ? (
18
+ // use a reduction for 2+ args
19
+ args.reduce((acc, x) => fn(acc, x), first)
20
+ ) : (
21
+ // apply special case unary function
22
+ fn1(first)
23
+ );
24
+ };
25
+ const ENV = {
26
+ "+": __mathOp(add, (x) => x),
27
+ "-": __mathOp(sub, (x) => sub(quantity(0, x.value), x)),
28
+ "*": __mathOp(mul, (x) => x),
29
+ "/": __mathOp(div, reciprocal),
30
+ area: (src) => {
31
+ const [w, h] = src.value.map((x) => quantity(1, x));
32
+ return mul(w, h);
33
+ },
34
+ volume: (src) => {
35
+ const [w, h, d] = src.value.map((x) => quantity(1, x));
36
+ return mul(mul(w, h), d);
37
+ },
38
+ width: (src) => quantity(1, src.value[0]),
39
+ height: (src) => quantity(1, src.value[1]),
40
+ depth: (src) => quantity(1, src.value[2])
41
+ };
42
+ const interpret = runtime({
43
+ expr: ({ children: [x, ...args] }) => {
44
+ const name = x.value;
45
+ const $args = args.map((a) => interpret(a, null));
46
+ return ENV[name]?.apply(null, $args) ?? convert($args[0], asUnit(name));
47
+ },
48
+ sym: ({ value }) => {
49
+ const match = /^[-+]?[0-9.]+(e[+-]?\d+)?/.exec(value);
50
+ return match ? quantity(+match[0], asUnit(value.substring(match[0].length))) : CONST[value.toUpperCase()] ?? asUnit(value);
51
+ },
52
+ str: () => illegalArgs("string value"),
53
+ num: (x) => x.value
54
+ });
55
+ const $eval = (src) => parse(src).children.reduce((_, x) => interpret(x, null), null);
56
+ export {
57
+ $eval
58
+ };
package/index.d.ts CHANGED
@@ -1,27 +1,6 @@
1
1
  export * from "./api.js";
2
+ export * from "./dsl.js";
2
3
  export * from "./unit.js";
3
- export * from "./units/accel.js";
4
- export * from "./units/angle.js";
5
- export * from "./units/area.js";
6
- export * from "./units/data.js";
7
- export * from "./units/density.js";
8
- export * from "./units/electric.js";
9
- export * from "./units/energy.js";
10
- export * from "./units/force.js";
11
- export * from "./units/frequency.js";
12
- export * from "./units/length.js";
13
- export * from "./units/luminous.js";
14
- export * from "./units/mass.js";
15
- export * from "./units/parts.js";
16
- export * from "./units/power.js";
17
- export * from "./units/pressure.js";
18
- export * from "./units/substance.js";
19
- export * from "./units/temperature.js";
20
- export * from "./units/time.js";
21
- export * from "./units/velocity.js";
22
- export * from "./units/volume.js";
23
- export * from "./constants/densities.js";
24
- export * from "./constants/earth.js";
25
- export * from "./constants/paper-sizes.js";
26
- export * from "./constants/velocities.js";
4
+ export * from "./constants/index.js";
5
+ export * from "./units/index.js";
27
6
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,26 +1,5 @@
1
1
  export * from "./api.js";
2
+ export * from "./dsl.js";
2
3
  export * from "./unit.js";
3
- export * from "./units/accel.js";
4
- export * from "./units/angle.js";
5
- export * from "./units/area.js";
6
- export * from "./units/data.js";
7
- export * from "./units/density.js";
8
- export * from "./units/electric.js";
9
- export * from "./units/energy.js";
10
- export * from "./units/force.js";
11
- export * from "./units/frequency.js";
12
- export * from "./units/length.js";
13
- export * from "./units/luminous.js";
14
- export * from "./units/mass.js";
15
- export * from "./units/parts.js";
16
- export * from "./units/power.js";
17
- export * from "./units/pressure.js";
18
- export * from "./units/substance.js";
19
- export * from "./units/temperature.js";
20
- export * from "./units/time.js";
21
- export * from "./units/velocity.js";
22
- export * from "./units/volume.js";
23
- export * from "./constants/densities.js";
24
- export * from "./constants/earth.js";
25
- export * from "./constants/paper-sizes.js";
26
- export * from "./constants/velocities.js";
4
+ export * from "./constants/index.js";
5
+ export * from "./units/index.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@thi.ng/units",
3
- "version": "1.0.33",
4
- "description": "Extensible SI unit creation, conversions, quantities & calculations (incl. ~170 predefined units & constants)",
3
+ "version": "1.1.0",
4
+ "description": "Extensible SI unit creation, conversions, quantities & calculations (incl. List-like DSL and ~170 predefined units & constants)",
5
5
  "type": "module",
6
6
  "module": "./index.js",
7
7
  "typings": "./index.d.ts",
@@ -39,10 +39,11 @@
39
39
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@thi.ng/api": "^8.12.9",
43
- "@thi.ng/checks": "^3.7.25",
44
- "@thi.ng/equiv": "^2.1.99",
45
- "@thi.ng/errors": "^2.5.49"
42
+ "@thi.ng/api": "^8.12.11",
43
+ "@thi.ng/checks": "^3.8.1",
44
+ "@thi.ng/equiv": "^2.1.101",
45
+ "@thi.ng/errors": "^2.6.0",
46
+ "@thi.ng/sexpr": "^1.1.18"
46
47
  },
47
48
  "devDependencies": {
48
49
  "esbuild": "^0.27.0",
@@ -59,14 +60,17 @@
59
60
  "capacitance",
60
61
  "conversion",
61
62
  "current",
63
+ "dsl",
62
64
  "energy",
63
65
  "force",
64
66
  "frequency",
65
67
  "length",
68
+ "lisp",
66
69
  "mass",
67
70
  "math",
68
71
  "power",
69
72
  "resistance",
73
+ "s-expression",
70
74
  "si",
71
75
  "speed",
72
76
  "symbolic",
@@ -113,6 +117,9 @@
113
117
  "./constants/velocities": {
114
118
  "default": "./constants/velocities.js"
115
119
  },
120
+ "./dsl": {
121
+ "default": "./dsl.js"
122
+ },
116
123
  "./unit": {
117
124
  "default": "./unit.js"
118
125
  },
@@ -181,5 +188,5 @@
181
188
  "status": "beta",
182
189
  "year": 2021
183
190
  },
184
- "gitHead": "fdca77cabf47dd23a9ab17a5ca13e3060075c12c\n"
191
+ "gitHead": "e7a21b9d2a188fa04d4c893d8531c40fbc0f4c06\n"
185
192
  }
package/unit.d.ts CHANGED
@@ -133,6 +133,50 @@ export declare class Quantity<T extends number | number[]> implements IDeref<T>
133
133
  * @param unit
134
134
  */
135
135
  export declare const quantity: <T extends number | number[]>(value: T, unit: MaybeUnit) => Quantity<T>;
136
+ /**
137
+ * Polymorphic function to sum to compatible quantities. Throws an error if `a`
138
+ * and `b` are incompatible.
139
+ *
140
+ * @example
141
+ * ```ts tangle:../export/add.ts
142
+ * import { add, convert, quantity } from "@thi.ng/units";
143
+ *
144
+ * const a = quantity(10, "mm");
145
+ * const b = quantity(1, "in");
146
+ *
147
+ * console.log(convert(add(a, b), "mm"));
148
+ * // 35.4
149
+ * ```
150
+ *
151
+ * @param a
152
+ * @param b
153
+ */
154
+ export declare function add(a: Quantity<number>, b: Quantity<number>): Quantity<number>;
155
+ export declare function add(a: Quantity<number>, b: Quantity<number[]>): Quantity<number[]>;
156
+ export declare function add(a: Quantity<number[]>, b: Quantity<number>): Quantity<number[]>;
157
+ export declare function add(a: Quantity<number[]>, b: Quantity<number[]>): Quantity<number[]>;
158
+ /**
159
+ * Polymorphic function to subtract to compatible quantities. Throws an error if
160
+ * `a` and `b` are incompatible.
161
+ *
162
+ * @example
163
+ * ```ts tangle:../export/sub.ts
164
+ * import { sub, convert, quantity } from "@thi.ng/units";
165
+ *
166
+ * const a = quantity(10, "mm");
167
+ * const b = quantity(1, "in");
168
+ *
169
+ * console.log(convert(sub(a, b), "mm"));
170
+ * // -15.4
171
+ * ```
172
+ *
173
+ * @param a
174
+ * @param b
175
+ */
176
+ export declare function sub(a: Quantity<number>, b: Quantity<number>): Quantity<number>;
177
+ export declare function sub(a: Quantity<number>, b: Quantity<number[]>): Quantity<number[]>;
178
+ export declare function sub(a: Quantity<number[]>, b: Quantity<number>): Quantity<number[]>;
179
+ export declare function sub(a: Quantity<number[]>, b: Quantity<number[]>): Quantity<number[]>;
136
180
  /**
137
181
  * Polymorphic function. Derives a new quantity or unit as the product of the
138
182
  * given quantities/units.
package/unit.js CHANGED
@@ -29,7 +29,7 @@ const asUnit = (id) => {
29
29
  return PREFIXES[pre] !== void 0 ? prefix(pre, unit2) : !pre ? unit2 : illegalArgs(`unknown unit: ${id}`);
30
30
  }
31
31
  }
32
- for (let u in UNITS) {
32
+ for (const u in UNITS) {
33
33
  if (UNITS[u].name === id) return UNITS[u];
34
34
  }
35
35
  illegalArgs(`unknown unit: ${id}`);
@@ -49,6 +49,20 @@ class Quantity {
49
49
  const quantity = (value, unit2) => new Quantity(
50
50
  isNumber(value) ? mul(unit2, value) : value.map((x) => mul(unit2, x))
51
51
  );
52
+ function add(a, b) {
53
+ const $unit = unit(__qunit(a).dim, 1, 0, true);
54
+ const valA = convert(a, $unit);
55
+ const valB = convert(b, $unit);
56
+ const val = isArray(valA) ? isArray(valB) ? valA.map((x, i) => x + valB[i]) : valA.map((x) => x + valB) : isArray(valB) ? valB.map((x) => x + valA) : valA + valB;
57
+ return quantity(val, $unit);
58
+ }
59
+ function sub(a, b) {
60
+ const $unit = unit(__qunit(a).dim, 1, 0, true);
61
+ const valA = convert(a, $unit);
62
+ const valB = convert(b, $unit);
63
+ const val = isArray(valA) ? isArray(valB) ? valA.map((x, i) => x - valB[i]) : valA.map((x) => x - valB) : isArray(valB) ? valB.map((x) => x - valA) : valA - valB;
64
+ return quantity(val, $unit);
65
+ }
52
66
  function mul(a, b, coherent2 = false) {
53
67
  if (a instanceof Quantity) return __combineQ(mul, a, b);
54
68
  const $a = __ensureUnit(a);
@@ -134,7 +148,7 @@ const formatSI = (u) => {
134
148
  };
135
149
  const __ensureUnit = (x) => isString(x) ? asUnit(x) : x;
136
150
  const __oneHot = (x) => {
137
- const dims = new Array(7).fill(0);
151
+ const dims = [0, 0, 0, 0, 0, 0, 0];
138
152
  dims[x] = 1;
139
153
  return dims;
140
154
  };
@@ -150,6 +164,7 @@ const __combineQ = (op, a, b) => {
150
164
  export {
151
165
  Quantity,
152
166
  UNITS,
167
+ add,
153
168
  asUnit,
154
169
  coherent,
155
170
  convert,
@@ -165,5 +180,6 @@ export {
165
180
  prefix,
166
181
  quantity,
167
182
  reciprocal,
183
+ sub,
168
184
  unit
169
185
  };
@@ -1,3 +1,8 @@
1
+ export declare const g_m2: import("../api.js").NamedUnit;
2
+ /**
3
+ * Alias for {@link g_m2}
4
+ */
5
+ export declare const gsm: import("../api.js").NamedUnit;
1
6
  export declare const kg_m3: import("../api.js").NamedUnit;
2
7
  export declare const dpi: import("../api.js").NamedUnit;
3
8
  //# sourceMappingURL=density.d.ts.map
package/units/density.js CHANGED
@@ -1,10 +1,15 @@
1
- import { inch } from "./length.js";
2
- import { kg } from "./mass.js";
3
1
  import { defUnit, div, reciprocal } from "../unit.js";
2
+ import { m2 } from "./area.js";
3
+ import { inch } from "./length.js";
4
+ import { g, kg } from "./mass.js";
4
5
  import { m3 } from "./volume.js";
6
+ const g_m2 = defUnit("g/m2", "gram per square meter", div(g, m2));
7
+ const gsm = defUnit("gsm", "gram per square meter", div(g, m2));
5
8
  const kg_m3 = defUnit("kg/m3", "kilogram per cubic meter", div(kg, m3));
6
9
  const dpi = defUnit("dpi", "dots per inch", reciprocal(inch));
7
10
  export {
8
11
  dpi,
12
+ g_m2,
13
+ gsm,
9
14
  kg_m3
10
15
  };
@@ -0,0 +1,21 @@
1
+ export * from "./accel.js";
2
+ export * from "./angle.js";
3
+ export * from "./area.js";
4
+ export * from "./data.js";
5
+ export * from "./density.js";
6
+ export * from "./electric.js";
7
+ export * from "./energy.js";
8
+ export * from "./force.js";
9
+ export * from "./frequency.js";
10
+ export * from "./length.js";
11
+ export * from "./luminous.js";
12
+ export * from "./mass.js";
13
+ export * from "./parts.js";
14
+ export * from "./power.js";
15
+ export * from "./pressure.js";
16
+ export * from "./substance.js";
17
+ export * from "./temperature.js";
18
+ export * from "./time.js";
19
+ export * from "./velocity.js";
20
+ export * from "./volume.js";
21
+ //# sourceMappingURL=index.d.ts.map
package/units/index.js ADDED
@@ -0,0 +1,20 @@
1
+ export * from "./accel.js";
2
+ export * from "./angle.js";
3
+ export * from "./area.js";
4
+ export * from "./data.js";
5
+ export * from "./density.js";
6
+ export * from "./electric.js";
7
+ export * from "./energy.js";
8
+ export * from "./force.js";
9
+ export * from "./frequency.js";
10
+ export * from "./length.js";
11
+ export * from "./luminous.js";
12
+ export * from "./mass.js";
13
+ export * from "./parts.js";
14
+ export * from "./power.js";
15
+ export * from "./pressure.js";
16
+ export * from "./substance.js";
17
+ export * from "./temperature.js";
18
+ export * from "./time.js";
19
+ export * from "./velocity.js";
20
+ export * from "./volume.js";