onejs-sl 0.1.2 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3
4
+
5
+ `onejs-sl/tables` names every parameter, so an editor can offer `lerp(x, y, s)` rather than an argument count. Nothing a program compiles to changes.
6
+
7
+ - `BUILTIN_PARAMS` names every builtin's parameters
8
+ - `SL_SDF_PARAM_NAMES` names every shape's parameters
9
+ - `LIB_SIGNATURES` gives every library function's parameter names and types
10
+
3
11
  ## 0.1.2
4
12
 
5
13
  The helper library has one source, `lib/*.hlsl`, translated at build time into GLSL ES, WGSL and the shared subset and copied into OneJS as its `.cginc` files. The web draws exactly what it drew; the Unity shader's text changes where Metal needed it to, and draws the same.
package/README.md CHANGED
@@ -37,7 +37,7 @@ only what it calls.
37
37
  |---|---|
38
38
  | `onejs-sl` | `parse` and `analyze`, the TypeScript form `sl`, the IR types, `SL_IR_VERSION`, `toJSON`/`fromJSON`, `SLParseError` (file, line, column, length) |
39
39
  | `onejs-sl/core` | the same without the parser: what a game needs at run time |
40
- | `onejs-sl/tables` | `BUILTINS`, `SL_HLSL`, `INPUTS`, `SL_SDF_SHAPES`, `SL_SDF_PARAMS`: what completion and highlighting read |
40
+ | `onejs-sl/tables` | `BUILTINS`, `SL_HLSL`, `INPUTS`, `SL_SDF_SHAPES`, `SL_SDF_PARAMS`: what completion and highlighting read. `BUILTIN_PARAMS`, `SL_SDF_PARAM_NAMES` and `LIB_SIGNATURES` name every parameter, so an editor can show `lerp(x, y, s)` |
41
41
  | `onejs-sl/limits` | `vmFit(program)`: whether the VM runs it, and why not, without encoding |
42
42
  | `onejs-sl/vm` | `encode`, `SL_WIRE_VERSION` |
43
43
  | `onejs-sl/emit/hlsl-body` | `emitBody`: a program as a function body for a host's own frame; `emitLibrary`: the library functions it calls |
@@ -92,6 +92,8 @@ by `npm run lib`, and nobody edits the copies:
92
92
  the shape table read from `sl_sdfDistance`'s switch), and the text of each
93
93
  function as GLSL ES (`glsl.ts`), WGSL (`wgsl.ts`) and the shared subset
94
94
  (`hlsl.ts`). The web emitters and `emitLibrary` take only what a program calls.
95
+ `names.ts` holds each function's parameter names, which only
96
+ `onejs-sl/tables` reads.
95
97
 
96
98
  `lib/translate.ts` does the printing, at build time only. It reads a subset of
97
99
  HLSL, listed at its top (functions, locals, `if`, bounded `for`, `switch`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onejs-sl",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "The OneJS shader language: a small typed language for per pixel programs, compiled to a portable IR and emitted as HLSL, WGSL, GLSL ES or a VM buffer",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -0,0 +1,77 @@
1
+ // GENERATED by lib/generate.ts from lib/*.hlsl. Do not edit: change the
2
+ // .hlsl and run `npm run lib`. src/lib/lib.test.ts fails when this is stale.
3
+
4
+ /** Parameter names as the source writes them, by `LIB_FUNCTIONS` index. `onejs-sl/tables` reads them. */
5
+ export const LIB_PARAM_NAMES: ReadonlyArray<readonly string[]> = [
6
+ ["x", "y"],
7
+ ["a", "b"],
8
+ ["v"],
9
+ ["p", "r"],
10
+ ["p", "b", "r"],
11
+ ["p", "b"],
12
+ ["p", "a", "b", "th"],
13
+ ["p", "a", "b"],
14
+ ["p", "b"],
15
+ ["p", "r1", "r2", "he"],
16
+ ["p", "wi", "he", "sk"],
17
+ ["p", "r"],
18
+ ["p", "q"],
19
+ ["p", "p0", "p1", "p2"],
20
+ ["p", "r1", "r2", "h"],
21
+ ["p", "r"],
22
+ ["p", "r"],
23
+ ["p", "r"],
24
+ ["p", "r"],
25
+ ["p", "r", "rf"],
26
+ ["p", "r", "n", "m"],
27
+ ["p", "c", "r"],
28
+ ["p", "r", "h"],
29
+ ["p", "sc", "ra", "rb"],
30
+ ["p", "n", "r", "th"],
31
+ ["p", "c", "r", "w"],
32
+ ["p", "r", "d"],
33
+ ["p", "a", "b", "w"],
34
+ ["p", "d", "ra", "rb"],
35
+ ["p", "h"],
36
+ ["p", "he", "ra", "rb", "bu"],
37
+ ["p"],
38
+ ["p", "b", "r"],
39
+ ["p", "w", "r"],
40
+ ["p", "ab"],
41
+ ["pos", "k"],
42
+ ["pos", "wi", "he"],
43
+ ["pos", "A", "B", "C"],
44
+ ["pos", "he"],
45
+ ["p", "wh"],
46
+ ["p", "wh", "n"],
47
+ ["p"],
48
+ ["p", "k", "he"],
49
+ ["p"],
50
+ ["p", "tb", "ra"],
51
+ ["p", "seed"],
52
+ ["p", "seed"],
53
+ ["p", "seed", "octaves", "lacunarity", "gain"],
54
+ ["p", "seed", "octaves"],
55
+ ["x"],
56
+ ["x"],
57
+ ["x"],
58
+ ["v"],
59
+ ["p", "seed"],
60
+ ["p", "seed", "octaves", "lacunarity", "gain"],
61
+ ["p", "seed", "octaves", "lacunarity", "gain"],
62
+ ["p", "seed", "octaves", "lacunarity", "gain"],
63
+ ["kind", "p", "seed", "octaves", "lacunarity", "gain"],
64
+ ["p"],
65
+ ["p"],
66
+ ["p"],
67
+ ["p", "octaves", "kind"],
68
+ ["c"],
69
+ ["id", "p", "a", "b"],
70
+ ["p"],
71
+ ["c"],
72
+ ["c"],
73
+ ["c"],
74
+ ["c"],
75
+ ["c"],
76
+ ["c"],
77
+ ]
package/src/params.ts ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Parameter names, for an editor's completions and signature help: so it can
3
+ * offer `lerp(x, y, s)` rather than "lerp takes 3 arguments".
4
+ *
5
+ * Metadata only. Nothing that checks or emits a program reads this file, and
6
+ * only `onejs-sl/tables` imports it, so it cannot change what any program
7
+ * compiles to. Each table is built by a call marked pure, so a bundle that
8
+ * imports `onejs-sl/tables` for BUILTINS alone (the Play editor's parser) drops
9
+ * all of it.
10
+ *
11
+ * The intrinsics carry the names the HLSL documentation gives them. Everything
12
+ * the library implements is named as `lib/*.hlsl` writes it, read by the
13
+ * translator into `src/lib/names.ts` when `npm run lib` runs, so there is no
14
+ * second list of those to drift: a builtin that lowers to a library helper takes
15
+ * that helper's names, and a shape's parameters are named after its function's
16
+ * parameters, in the slots `sl_sdfDistance` passes them in.
17
+ */
18
+
19
+ import { BUILTINS } from "./lang/builtins"
20
+ import { LIB_FUNCTIONS, SDF_CALLS } from "./lib/table"
21
+ import { LIB_PARAM_NAMES } from "./lib/names"
22
+ import { SL_SDF_PARAMS, SL_SDF_SHAPES, type SlSdfKind } from "./shapes"
23
+
24
+ /** One function of the helper library, as its source declares it. */
25
+ export interface LibSignature {
26
+ name: string
27
+ params: ReadonlyArray<{ name: string; type: string }>
28
+ ret: string
29
+ }
30
+
31
+ /** Every function in the helper library, by `LIB_FUNCTIONS` index, overloads included. */
32
+ export const LIB_SIGNATURES: readonly LibSignature[] = /* @__PURE__ */ LIB_FUNCTIONS.map((f, i) => ({
33
+ name: f.name,
34
+ params: f.params.map((type, j) => ({ name: LIB_PARAM_NAMES[i]![j]!, type })),
35
+ ret: f.ret,
36
+ }))
37
+
38
+ /** A library function's parameter names, the same for every overload of it. */
39
+ function namesOf(fn: string): readonly string[] {
40
+ const found = LIB_SIGNATURES.filter((s) => s.name === fn).map((s) => s.params.map((p) => p.name).join(", "))
41
+ if (found.length === 0) throw new Error(`the library has no ${fn}`)
42
+ if (new Set(found).size > 1) throw new Error(`${fn}'s overloads name their parameters differently: ${found.join(" / ")}`)
43
+ return found[0]!.split(", ")
44
+ }
45
+
46
+ /** The HLSL documentation's names. */
47
+ const INTRINSIC: Record<string, readonly string[]> = {
48
+ sin: ["x"], cos: ["x"], tan: ["x"], asin: ["x"], acos: ["x"], exp: ["x"], log: ["x"], sqrt: ["x"],
49
+ sign: ["x"], ceil: ["x"], round: ["x"], abs: ["x"], floor: ["x"], frac: ["x"], saturate: ["x"],
50
+ rcp: ["x"], normalize: ["x"], length: ["x"],
51
+ pow: ["x", "y"], min: ["x", "y"], max: ["x", "y"], clamp: ["x", "min", "max"], atan2: ["y", "x"],
52
+ distance: ["x", "y"], dot: ["x", "y"], cross: ["x", "y"], reflect: ["i", "n"],
53
+ lerp: ["x", "y", "s"], step: ["y", "x"], smoothstep: ["min", "max", "x"],
54
+ tex2D: ["s", "t"],
55
+ }
56
+
57
+ /**
58
+ * The library helper each builtin lowers to (`emitBody` calls it, and a test
59
+ * checks that it does), and how many of its parameters an author writes: fbm's
60
+ * third, the kind, is the opcode's own.
61
+ */
62
+ export const LIBRARY: Readonly<Record<string, [string, number]>> = {
63
+ toLinear: ["sl_toLinear", 1], hsv2rgb: ["sl_hsv2rgb", 1], luminance: ["sl_luminance", 1],
64
+ noise: ["sl_valueNoise", 1], simplex: ["sl_simplex", 1], voronoi: ["sl_voronoi", 1],
65
+ fbm: ["sl_fbm", 2], turbulence: ["sl_fbm", 2], ridged: ["sl_fbm", 2],
66
+ // The point; the shape's own parameters are SL_SDF_PARAM_NAMES.
67
+ sdf: ["sl_sdfDistance", 0],
68
+ }
69
+
70
+ /** The two the language defines itself, named as the EDSL's `sl.remap` and `sl.ramp` name theirs. */
71
+ const MACRO: Record<string, readonly string[]> = {
72
+ remap: ["v", "fromMin", "fromMax", "toMin", "toMax"],
73
+ // At least two stops, and as many more as `max` allows.
74
+ ramp: ["t", "stop", "stop..."],
75
+ }
76
+
77
+ /**
78
+ * Each builtin's parameter names, one per argument up to its `max`; those past
79
+ * its `min` are optional. A name ending in `...` repeats up to `max`. `sdf` is
80
+ * written `sdf.circle(p, r)`: its entry names the point, and the shape's own
81
+ * parameters are `SL_SDF_PARAM_NAMES[shape]`.
82
+ */
83
+ export const BUILTIN_PARAMS: Readonly<Record<string, readonly string[]>> = /* @__PURE__ */ Object.fromEntries(
84
+ Object.keys(BUILTINS).map((name) => {
85
+ const lib = LIBRARY[name]
86
+ if (name === "sdf") return [name, [namesOf(lib![0])[1]!]]
87
+ const names = INTRINSIC[name] ?? MACRO[name] ?? (lib && namesOf(lib[0]).slice(0, lib[1]))
88
+ if (names === undefined) throw new Error(`builtin ${name} has no parameter names in params.ts`)
89
+ return [name, names]
90
+ }),
91
+ )
92
+
93
+ /**
94
+ * Each shape's parameters, in the order `sdf.<shape>(p, ...)` takes them, named
95
+ * after its function's: a vector parameter's components as `b.x`, `b.y`.
96
+ */
97
+ export const SL_SDF_PARAM_NAMES: Readonly<Record<SlSdfKind, readonly string[]>> = /* @__PURE__ */ shapeNames()
98
+
99
+ function shapeNames(): Record<SlSdfKind, readonly string[]> {
100
+ const out = {} as Record<SlSdfKind, readonly string[]>
101
+ for (const shape of Object.keys(SL_SDF_SHAPES) as SlSdfKind[]) {
102
+ const call = SDF_CALLS[SL_SDF_SHAPES[shape]]!
103
+ // The function's first parameter is the point, which `sl_sdfDistance` passes itself.
104
+ const fn = namesOf(call.fn).slice(1)
105
+ const slots = new Array<string>(SL_SDF_PARAMS[shape])
106
+ call.args.forEach((arg, i) => {
107
+ const at = "int" in arg ? [arg.int] : arg
108
+ at.forEach((slot, j) => { slots[slot] = at.length === 1 ? fn[i]! : `${fn[i]}.${"xyzw"[j]}` })
109
+ })
110
+ out[shape] = slots
111
+ }
112
+ return out
113
+ }
package/src/tables.ts CHANGED
@@ -2,7 +2,8 @@
2
2
  * `onejs-sl/tables`: what completion and highlighting read.
3
3
  *
4
4
  * The same tables the checker and the emitters use, so an editor offers
5
- * exactly the builtins that compile, with the arities they take.
5
+ * exactly the builtins that compile, with the arities they take and the names
6
+ * of their parameters (`params.ts`), and the helper library's signatures.
6
7
  */
7
8
  export { BUILTINS, NOT_YET } from "./lang/builtins"
8
9
  export type { Builtin } from "./lang/builtins"
@@ -12,4 +13,6 @@ export {
12
13
  export type { SLSurface } from "./ops"
13
14
  export { INPUTS } from "./ir"
14
15
  export { SL_SDF_SHAPES, SL_SDF_PARAMS } from "./shapes"
16
+ export { BUILTIN_PARAMS, LIB_SIGNATURES, SL_SDF_PARAM_NAMES } from "./params"
17
+ export type { LibSignature } from "./params"
15
18
  export type { SlSdfKind } from "./shapes"