tjs-lang 0.9.0 → 0.9.1
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/CLAUDE.md +7 -1
- package/dist/index.js +111 -107
- package/dist/index.js.map +3 -3
- package/dist/src/lang/emitters/js-wasm.d.ts +2 -0
- package/dist/src/lang/parser-types.d.ts +7 -0
- package/dist/tjs-browser-from-ts.js +1 -1
- package/dist/tjs-browser-from-ts.js.map +1 -1
- package/dist/tjs-browser.js +93 -89
- package/dist/tjs-browser.js.map +3 -3
- package/dist/tjs-eval.js +14 -14
- package/dist/tjs-eval.js.map +2 -2
- package/dist/tjs-from-ts.js +1 -1
- package/dist/tjs-from-ts.js.map +1 -1
- package/dist/tjs-lang.js +94 -90
- package/dist/tjs-lang.js.map +3 -3
- package/dist/tjs-runtime.js +1 -1
- package/dist/tjs-runtime.js.map +1 -1
- package/dist/tjs-vm.js +2 -2
- package/dist/tjs-vm.js.map +2 -2
- package/docs/type-system-north-star.md +100 -0
- package/package.json +1 -1
- package/src/lang/emitters/js-wasm.ts +8 -4
- package/src/lang/emitters/js.ts +36 -11
- package/src/lang/parser-transforms.ts +4 -1
- package/src/lang/parser-types.ts +7 -0
- package/src/lang/parser.ts +9 -1
- package/src/lang/predicate-report.test.ts +24 -0
- package/src/lang/wasm-fallback-warning.test.ts +50 -0
- package/src/lang/wasm-intdiv-lint.test.ts +45 -0
- package/src/lang/wasm-ready.test.ts +94 -0
- package/src/lang/wasm-simd-ops.test.ts +84 -0
- package/src/lang/wasm.ts +61 -2
package/src/lang/wasm.ts
CHANGED
|
@@ -262,6 +262,19 @@ const SimdOp = {
|
|
|
262
262
|
f32x4_sub: 0xe5,
|
|
263
263
|
f32x4_mul: 0xe6,
|
|
264
264
|
f32x4_div: 0xe7,
|
|
265
|
+
f32x4_min: 0xe8,
|
|
266
|
+
f32x4_max: 0xe9,
|
|
267
|
+
|
|
268
|
+
// f32x4 comparisons → v128 lane mask (all-1s / all-0s per lane)
|
|
269
|
+
f32x4_eq: 0x41,
|
|
270
|
+
f32x4_ne: 0x42,
|
|
271
|
+
f32x4_lt: 0x43,
|
|
272
|
+
f32x4_gt: 0x44,
|
|
273
|
+
f32x4_le: 0x45,
|
|
274
|
+
f32x4_ge: 0x46,
|
|
275
|
+
|
|
276
|
+
// Lane-wise blend: bitselect(a, b, mask) — bits of a where mask=1, else b.
|
|
277
|
+
v128_bitselect: 0x52,
|
|
265
278
|
} as const
|
|
266
279
|
|
|
267
280
|
/** Reverse lookup for SIMD opcodes */
|
|
@@ -1160,6 +1173,18 @@ function compileBinaryExpr(
|
|
|
1160
1173
|
return [Op.f64_const, ...encodeF64(0)]
|
|
1161
1174
|
}
|
|
1162
1175
|
|
|
1176
|
+
// Lint the i32/i32 division footgun: `/` with two integer operands (loop vars,
|
|
1177
|
+
// `0`-annotated params, int literals) does TRUNCATING integer division, and the
|
|
1178
|
+
// coercion to f64 only happens at the *next* operator — so `x / w - 0.5` is
|
|
1179
|
+
// silently `0 - 0.5` for all `x < w`. Warn once per block; add `+ 0.0` to an
|
|
1180
|
+
// operand to force f64 division. (UI-#4 — this is a warning, not an error:
|
|
1181
|
+
// integer division may be intended.)
|
|
1182
|
+
if (node.operator === '/' && opType === 'i32') {
|
|
1183
|
+
const msg =
|
|
1184
|
+
"integer division: '/' with two i32 operands truncates (result coerces to f64 only at the next operator). Add `+ 0.0` to an operand to force f64 division."
|
|
1185
|
+
if (!ctx.warnings.includes(msg)) ctx.warnings.push(msg)
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1163
1188
|
return [...leftCode, ...rightCode, opcode]
|
|
1164
1189
|
}
|
|
1165
1190
|
|
|
@@ -1808,8 +1833,17 @@ function compileSIMDCall(
|
|
|
1808
1833
|
case 'f32x4_add':
|
|
1809
1834
|
case 'f32x4_sub':
|
|
1810
1835
|
case 'f32x4_mul':
|
|
1811
|
-
case 'f32x4_div':
|
|
1812
|
-
|
|
1836
|
+
case 'f32x4_div':
|
|
1837
|
+
case 'f32x4_min':
|
|
1838
|
+
case 'f32x4_max':
|
|
1839
|
+
case 'f32x4_eq':
|
|
1840
|
+
case 'f32x4_ne':
|
|
1841
|
+
case 'f32x4_lt':
|
|
1842
|
+
case 'f32x4_gt':
|
|
1843
|
+
case 'f32x4_le':
|
|
1844
|
+
case 'f32x4_ge': {
|
|
1845
|
+
// Binary v128 op: f(a, b) → v128. The comparisons (eq/ne/lt/gt/le/ge)
|
|
1846
|
+
// return a v128 lane mask (all-1s where true, all-0s else).
|
|
1813
1847
|
code.push(...compileExpression(args[0], ctx))
|
|
1814
1848
|
code.push(...compileExpression(args[1], ctx))
|
|
1815
1849
|
const opMap: Record<string, number> = {
|
|
@@ -1817,11 +1851,36 @@ function compileSIMDCall(
|
|
|
1817
1851
|
f32x4_sub: SimdOp.f32x4_sub,
|
|
1818
1852
|
f32x4_mul: SimdOp.f32x4_mul,
|
|
1819
1853
|
f32x4_div: SimdOp.f32x4_div,
|
|
1854
|
+
f32x4_min: SimdOp.f32x4_min,
|
|
1855
|
+
f32x4_max: SimdOp.f32x4_max,
|
|
1856
|
+
f32x4_eq: SimdOp.f32x4_eq,
|
|
1857
|
+
f32x4_ne: SimdOp.f32x4_ne,
|
|
1858
|
+
f32x4_lt: SimdOp.f32x4_lt,
|
|
1859
|
+
f32x4_gt: SimdOp.f32x4_gt,
|
|
1860
|
+
f32x4_le: SimdOp.f32x4_le,
|
|
1861
|
+
f32x4_ge: SimdOp.f32x4_ge,
|
|
1820
1862
|
}
|
|
1821
1863
|
code.push(...encodeSIMD(opMap[name]))
|
|
1822
1864
|
return code
|
|
1823
1865
|
}
|
|
1824
1866
|
|
|
1867
|
+
case 'f32x4_select': {
|
|
1868
|
+
// Branch-free lane blend: f32x4_select(mask, a, b) → a where the mask lane
|
|
1869
|
+
// is true (all-1s, e.g. from a compare), b where false. Maps to
|
|
1870
|
+
// v128.bitselect(a, b, mask) — bits of a where mask=1, of b where mask=0.
|
|
1871
|
+
if (args.length !== 3) {
|
|
1872
|
+
ctx.errors.push(
|
|
1873
|
+
`f32x4_select expects (mask, a, b) — 3 args, got ${args.length}`
|
|
1874
|
+
)
|
|
1875
|
+
return [Op.f64_const, ...encodeF64(0)]
|
|
1876
|
+
}
|
|
1877
|
+
code.push(...compileExpression(args[1], ctx)) // a (bitselect v1)
|
|
1878
|
+
code.push(...compileExpression(args[2], ctx)) // b (bitselect v2)
|
|
1879
|
+
code.push(...compileExpression(args[0], ctx)) // mask
|
|
1880
|
+
code.push(...encodeSIMD(SimdOp.v128_bitselect))
|
|
1881
|
+
return code
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1825
1884
|
case 'f32x4_neg':
|
|
1826
1885
|
case 'f32x4_sqrt': {
|
|
1827
1886
|
// Unary v128 op: f32x4_neg(a) → v128
|