@thi.ng/shader-ast-js 1.0.26 → 1.0.28

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/target.js CHANGED
@@ -1,232 +1,220 @@
1
1
  import { isBoolean } from "@thi.ng/checks/is-boolean";
2
2
  import { isNumber } from "@thi.ng/checks/is-number";
3
3
  import { unsupported } from "@thi.ng/errors/unsupported";
4
- import { isBool, isInt, isMat, isUint, isVec, } from "@thi.ng/shader-ast/ast/checks";
4
+ import {
5
+ isBool,
6
+ isInt,
7
+ isMat,
8
+ isUint,
9
+ isVec
10
+ } from "@thi.ng/shader-ast/ast/checks";
5
11
  import { defTarget } from "@thi.ng/shader-ast/target";
6
12
  import { JS_DEFAULT_ENV } from "./env.js";
7
13
  const CMP_OPS = {
8
- "!": "not",
9
- "<": "lt",
10
- "<=": "lte",
11
- "==": "eq",
12
- "!=": "neq",
13
- ">=": "gte",
14
- ">": "gt",
14
+ "!": "not",
15
+ "<": "lt",
16
+ "<=": "lte",
17
+ "==": "eq",
18
+ "!=": "neq",
19
+ ">=": "gte",
20
+ ">": "gt"
15
21
  };
16
22
  const OP_IDS = {
17
- ...CMP_OPS,
18
- "+": "add",
19
- "-": "sub",
20
- "*": "mul",
21
- "/": "div",
22
- "%": "modi",
23
- "++": "inc",
24
- "--": "dec",
25
- "||": "or",
26
- "&&": "and",
27
- "|": "bitor",
28
- "&": "bitand",
29
- "^": "bitxor",
30
- "~": "bitnot",
31
- "<<": "lshift",
32
- ">>": "rshift",
23
+ ...CMP_OPS,
24
+ "+": "add",
25
+ "-": "sub",
26
+ "*": "mul",
27
+ "/": "div",
28
+ "%": "modi",
29
+ "++": "inc",
30
+ "--": "dec",
31
+ "||": "or",
32
+ "&&": "and",
33
+ "|": "bitor",
34
+ "&": "bitand",
35
+ "^": "bitxor",
36
+ "~": "bitnot",
37
+ "<<": "lshift",
38
+ ">>": "rshift"
33
39
  };
34
40
  const VEC_TYPES = [
35
- "vec2",
36
- "vec3",
37
- "vec4",
38
- "ivec2",
39
- "ivec3",
40
- "ivec4",
41
- "uvec2",
42
- "uvec3",
43
- "uvec4",
41
+ "vec2",
42
+ "vec3",
43
+ "vec4",
44
+ "ivec2",
45
+ "ivec3",
46
+ "ivec4",
47
+ "uvec2",
48
+ "uvec3",
49
+ "uvec4"
44
50
  ];
45
51
  const PRELUDE = [
46
- "float",
47
- "int",
48
- "uint",
49
- ...VEC_TYPES,
50
- "bvec2",
51
- "bvec3",
52
- "bvec4",
53
- "mat2",
54
- "mat3",
55
- "mat4",
56
- "sampler2D",
57
- "sampler3D",
58
- "samplerCube",
59
- "sampler2DShadow",
60
- "samplerCubeShadow",
61
- ]
62
- .map((x) => `const ${x} = env.${x};`)
63
- .join("\n");
64
- const POOL_PRELUDE = VEC_TYPES.map((x) => `const $${x} = env.pools.${x}.from;`).join("\n");
52
+ "float",
53
+ "int",
54
+ "uint",
55
+ ...VEC_TYPES,
56
+ "bvec2",
57
+ "bvec3",
58
+ "bvec4",
59
+ "mat2",
60
+ "mat3",
61
+ "mat4",
62
+ "sampler2D",
63
+ "sampler3D",
64
+ "samplerCube",
65
+ "sampler2DShadow",
66
+ "samplerCubeShadow"
67
+ ].map((x) => `const ${x} = env.${x};`).join("\n");
68
+ const POOL_PRELUDE = VEC_TYPES.map(
69
+ (x) => `const $${x} = env.pools.${x}.from;`
70
+ ).join("\n");
65
71
  const COMPS = { x: 0, y: 1, z: 2, w: 3 };
66
72
  const RE_SEMI = /[};]$/;
67
73
  const RESET = `for(let t in env.pools) env.pools[t].reset();`;
68
74
  const isIntOrBool = (l) => isInt(l) || isUint(l) || isBool(l);
69
75
  const isVecOrMat = (l) => isVec(l) || isMat(l);
70
76
  const swizzle = (id) => [...id].map((x) => COMPS[x]).join(", ");
71
- const buildComments = (t) => `/**\n${t.args.map((p) => ` * @param ${p.id} ${p.type}`).join("\n")}\n */`;
72
- const buildExports = (tree) => tree.tag === "scope"
73
- ? tree.body
74
- .filter((x) => x.tag === "fn")
75
- .map((f) => `${f.id}: ${f.id}`)
76
- .join(",\n")
77
- : tree.tag === "fn"
78
- ? `${tree.id}: ${tree.id}`
79
- : "";
80
- export const targetJS = (opts) => {
81
- opts = { ...opts };
82
- const ff = opts.prec !== undefined
83
- ? (x) => (x === (x | 0) ? x : x.toFixed(opts.prec))
84
- : String;
85
- const $list = (body, sep = ", ") => body.map(emit).join(sep);
86
- const $fn = (name, args) => `${name}(${$list(args)})`;
87
- const $vecFromPool = ({ val, info, type }) => !info ? `$${type}(${$list(val)})` : `env.${type}${info}(${$list(val)})`;
88
- const $vec = ({ val, info, type }) => !info ? `[${$list(val)}]` : `env.${type}${info}(${$list(val)})`;
89
- const $num = (v, f) => isNumber(v) ? String(v) : f(v);
90
- const $float = (v, f) => (isNumber(v) ? ff(v) : f(v));
91
- const emit = defTarget({
92
- arg: (t) => t.id,
93
- array_init: (t) => `[${$list(t.init)}]`,
94
- assign: (t) => {
95
- const rhs = emit(t.r);
96
- if (t.l.tag === "swizzle") {
97
- const s = t.l;
98
- const id = swizzle(s.id);
99
- const val = emit(s.val);
100
- return s.id.length > 1
101
- ? `env.set_swizzle${s.id.length}(${val}, ${rhs}, ${id})`
102
- : `(${val}[${id}] = ${rhs})`;
103
- }
104
- return `${emit(t.l)} = ${rhs}`;
105
- },
106
- ctrl: (t) => t.id,
107
- call: (t) => $fn(t.id, t.args),
108
- call_i: (t) => $fn(`${t.args[0].type}.${t.id}${t.info || ""}`, t.args),
109
- decl: ({ type, id }) => {
110
- const res = [];
111
- res.push(id.opts.const ? "const" : "let", `/*${type}*/`, id.id);
112
- id.init
113
- ? res.push(`= ${emit(id.init)}`)
114
- : id.opts.num !== undefined
115
- ? res.push(`= new Array(${id.opts.num})`)
116
- : undefined;
117
- return res.join(" ");
118
- },
119
- fn: (t) => {
120
- let body;
121
- if (t.id === "main") {
122
- body = `{\n${RESET}\n${emit({
123
- ...t.scope,
124
- global: true,
125
- })}}`;
126
- }
127
- else {
128
- body = emit(t.scope);
129
- }
130
- return `${buildComments(t)}\nfunction ${t.id}(${$list(t.args)}) ${body}`;
131
- },
132
- for: (t) => `for(${t.init ? emit(t.init) : ""}; ${emit(t.test)}; ${t.iter ? emit(t.iter) : ""}) ${emit(t.scope)}`,
133
- idx: (t) => `${emit(t.val)}[${emit(t.id)}]`,
134
- idxm: (t) => `${t.val.type}.idx(${emit(t.val)},${emit(t.id)})`,
135
- if: (t) => {
136
- const res = `if (${emit(t.test)}) ${emit(t.t)}`;
137
- return t.f ? `${res} else ${emit(t.f)}` : res;
138
- },
139
- lit: (t) => {
140
- const v = t.val;
141
- switch (t.type) {
142
- case "bool":
143
- return isBoolean(v) ? String(v) : `!!(${emit(v)})`;
144
- case "float":
145
- return $float(v, () => isBool(v) ? `(${emit(v)} & 1)` : emit(v));
146
- case "int":
147
- return $num(v, () => `(${emit(v)} | 0)`);
148
- case "uint":
149
- return $num(v, () => `(${emit(v)} >>> 0)`);
150
- case "vec2":
151
- case "vec3":
152
- case "vec4":
153
- case "ivec2":
154
- case "ivec3":
155
- case "ivec4":
156
- case "uvec2":
157
- case "uvec3":
158
- case "uvec4":
159
- return $vecFromPool(t);
160
- case "bvec2":
161
- case "bvec3":
162
- case "bvec4":
163
- case "mat2":
164
- case "mat3":
165
- case "mat4":
166
- return $vec(t);
167
- default:
168
- return unsupported(`unknown type: ${t.type}`);
169
- }
170
- },
171
- op1: (t) => {
172
- const complex = isVecOrMat(t) || isInt(t);
173
- const op = t.op;
174
- const val = emit(t.val);
175
- return complex && t.post
176
- ? `${t.val.id} = ${t.type}.${OP_IDS[op]}(${val})`
177
- : complex
178
- ? `${t.type}.${OP_IDS[op]}1(${val})`
179
- : t.post
180
- ? `(${val}${op})`
181
- : `${op}${val}`;
182
- },
183
- op2: (t) => {
184
- const { l, r } = t;
185
- const vec = isVecOrMat(l)
186
- ? l.type
187
- : isVecOrMat(r)
188
- ? r.type
189
- : undefined;
190
- const int = !vec
191
- ? isIntOrBool(l)
192
- ? l.type
193
- : isIntOrBool(r)
194
- ? r.type
195
- : undefined
196
- : undefined;
197
- const el = emit(l);
198
- const er = emit(r);
199
- return vec || (int && !CMP_OPS[t.op])
200
- ? `${vec || int}.${OP_IDS[t.op]}${t.info || ""}(${el}, ${er})`
201
- : `(${el} ${t.op} ${er})`;
202
- },
203
- ret: (t) => "return" + (t.val ? " " + emit(t.val) : ""),
204
- scope: (t) => {
205
- let res = $list(t.body, ";\n");
206
- res += t.body.length && !RE_SEMI.test(res) ? ";" : "";
207
- return t.global ? res : `{\n${res}\n}`;
208
- },
209
- swizzle: (t) => t.id.length > 1
210
- ? `env.swizzle${t.id.length}(${emit(t.val)}, ${swizzle(t.id)})`
211
- : `${emit(t.val)}[${swizzle(t.id)}]`,
212
- sym: (t) => t.id,
213
- ternary: (t) => `(${emit(t.test)} ? ${emit(t.t)} : ${emit(t.f)})`,
214
- while: (t) => `while (${emit(t.test)}) ${emit(t.scope)}`,
215
- });
216
- Object.assign(emit, {
217
- compile: (tree, env = JS_DEFAULT_ENV) => {
218
- const exports = buildExports(tree);
219
- return new Function("env", [
220
- PRELUDE,
221
- POOL_PRELUDE,
222
- emit(tree),
223
- "return {",
224
- `__reset: () => {${RESET}},`,
225
- `__stats: () => Object.entries(env.pools).reduce((acc, [k, v]) => (acc[k] = v.index, acc), {}),`,
226
- exports,
227
- "};",
228
- ].join("\n"))(env);
229
- },
230
- });
231
- return emit;
77
+ const buildComments = (t) => `/**
78
+ ${t.args.map((p) => ` * @param ${p.id} ${p.type}`).join("\n")}
79
+ */`;
80
+ const buildExports = (tree) => tree.tag === "scope" ? tree.body.filter((x) => x.tag === "fn").map((f) => `${f.id}: ${f.id}`).join(",\n") : tree.tag === "fn" ? `${tree.id}: ${tree.id}` : "";
81
+ const targetJS = (opts) => {
82
+ opts = { ...opts };
83
+ const ff = opts.prec !== void 0 ? (x) => x === (x | 0) ? x : x.toFixed(opts.prec) : String;
84
+ const $list = (body, sep = ", ") => body.map(emit).join(sep);
85
+ const $fn = (name, args) => `${name}(${$list(args)})`;
86
+ const $vecFromPool = ({ val, info, type }) => !info ? `$${type}(${$list(val)})` : `env.${type}${info}(${$list(val)})`;
87
+ const $vec = ({ val, info, type }) => !info ? `[${$list(val)}]` : `env.${type}${info}(${$list(val)})`;
88
+ const $num = (v, f) => isNumber(v) ? String(v) : f(v);
89
+ const $float = (v, f) => isNumber(v) ? ff(v) : f(v);
90
+ const emit = defTarget({
91
+ arg: (t) => t.id,
92
+ array_init: (t) => `[${$list(t.init)}]`,
93
+ assign: (t) => {
94
+ const rhs = emit(t.r);
95
+ if (t.l.tag === "swizzle") {
96
+ const s = t.l;
97
+ const id = swizzle(s.id);
98
+ const val = emit(s.val);
99
+ return s.id.length > 1 ? `env.set_swizzle${s.id.length}(${val}, ${rhs}, ${id})` : `(${val}[${id}] = ${rhs})`;
100
+ }
101
+ return `${emit(t.l)} = ${rhs}`;
102
+ },
103
+ ctrl: (t) => t.id,
104
+ call: (t) => $fn(t.id, t.args),
105
+ call_i: (t) => $fn(`${t.args[0].type}.${t.id}${t.info || ""}`, t.args),
106
+ decl: ({ type, id }) => {
107
+ const res = [];
108
+ res.push(id.opts.const ? "const" : "let", `/*${type}*/`, id.id);
109
+ id.init ? res.push(`= ${emit(id.init)}`) : id.opts.num !== void 0 ? res.push(`= new Array(${id.opts.num})`) : void 0;
110
+ return res.join(" ");
111
+ },
112
+ fn: (t) => {
113
+ let body;
114
+ if (t.id === "main") {
115
+ body = `{
116
+ ${RESET}
117
+ ${emit({
118
+ ...t.scope,
119
+ global: true
120
+ })}}`;
121
+ } else {
122
+ body = emit(t.scope);
123
+ }
124
+ return `${buildComments(t)}
125
+ function ${t.id}(${$list(
126
+ t.args
127
+ )}) ${body}`;
128
+ },
129
+ for: (t) => `for(${t.init ? emit(t.init) : ""}; ${emit(t.test)}; ${t.iter ? emit(t.iter) : ""}) ${emit(t.scope)}`,
130
+ idx: (t) => `${emit(t.val)}[${emit(t.id)}]`,
131
+ idxm: (t) => `${t.val.type}.idx(${emit(t.val)},${emit(t.id)})`,
132
+ if: (t) => {
133
+ const res = `if (${emit(t.test)}) ${emit(t.t)}`;
134
+ return t.f ? `${res} else ${emit(t.f)}` : res;
135
+ },
136
+ lit: (t) => {
137
+ const v = t.val;
138
+ switch (t.type) {
139
+ case "bool":
140
+ return isBoolean(v) ? String(v) : `!!(${emit(v)})`;
141
+ case "float":
142
+ return $float(
143
+ v,
144
+ () => isBool(v) ? `(${emit(v)} & 1)` : emit(v)
145
+ );
146
+ case "int":
147
+ return $num(v, () => `(${emit(v)} | 0)`);
148
+ case "uint":
149
+ return $num(v, () => `(${emit(v)} >>> 0)`);
150
+ case "vec2":
151
+ case "vec3":
152
+ case "vec4":
153
+ case "ivec2":
154
+ case "ivec3":
155
+ case "ivec4":
156
+ case "uvec2":
157
+ case "uvec3":
158
+ case "uvec4":
159
+ return $vecFromPool(t);
160
+ case "bvec2":
161
+ case "bvec3":
162
+ case "bvec4":
163
+ case "mat2":
164
+ case "mat3":
165
+ case "mat4":
166
+ return $vec(t);
167
+ default:
168
+ return unsupported(`unknown type: ${t.type}`);
169
+ }
170
+ },
171
+ op1: (t) => {
172
+ const complex = isVecOrMat(t) || isInt(t);
173
+ const op = t.op;
174
+ const val = emit(t.val);
175
+ return complex && t.post ? `${t.val.id} = ${t.type}.${OP_IDS[op]}(${val})` : complex ? `${t.type}.${OP_IDS[op]}1(${val})` : t.post ? `(${val}${op})` : `${op}${val}`;
176
+ },
177
+ op2: (t) => {
178
+ const { l, r } = t;
179
+ const vec = isVecOrMat(l) ? l.type : isVecOrMat(r) ? r.type : void 0;
180
+ const int = !vec ? isIntOrBool(l) ? l.type : isIntOrBool(r) ? r.type : void 0 : void 0;
181
+ const el = emit(l);
182
+ const er = emit(r);
183
+ return vec || int && !CMP_OPS[t.op] ? `${vec || int}.${OP_IDS[t.op]}${t.info || ""}(${el}, ${er})` : `(${el} ${t.op} ${er})`;
184
+ },
185
+ ret: (t) => "return" + (t.val ? " " + emit(t.val) : ""),
186
+ scope: (t) => {
187
+ let res = $list(t.body, ";\n");
188
+ res += t.body.length && !RE_SEMI.test(res) ? ";" : "";
189
+ return t.global ? res : `{
190
+ ${res}
191
+ }`;
192
+ },
193
+ swizzle: (t) => t.id.length > 1 ? `env.swizzle${t.id.length}(${emit(t.val)}, ${swizzle(t.id)})` : `${emit(t.val)}[${swizzle(t.id)}]`,
194
+ sym: (t) => t.id,
195
+ ternary: (t) => `(${emit(t.test)} ? ${emit(t.t)} : ${emit(t.f)})`,
196
+ while: (t) => `while (${emit(t.test)}) ${emit(t.scope)}`
197
+ });
198
+ Object.assign(emit, {
199
+ compile: (tree, env = JS_DEFAULT_ENV) => {
200
+ const exports = buildExports(tree);
201
+ return new Function(
202
+ "env",
203
+ [
204
+ PRELUDE,
205
+ POOL_PRELUDE,
206
+ emit(tree),
207
+ "return {",
208
+ `__reset: () => {${RESET}},`,
209
+ `__stats: () => Object.entries(env.pools).reduce((acc, [k, v]) => (acc[k] = v.index, acc), {}),`,
210
+ exports,
211
+ "};"
212
+ ].join("\n")
213
+ )(env);
214
+ }
215
+ });
216
+ return emit;
217
+ };
218
+ export {
219
+ targetJS
232
220
  };