Skip to content

Commit 8e32304

Browse files
Dale-Blackclaude
andcommitted
README: comprehensive transpilation coverage tables
Three-tier table: - Fully Transpiled: 80+ operations across types, 1D/ND arrays, higher-order, strings, math, broadcasting, control flow, structs, collections, IO, parsing - Not Yet Transpiled: matrix multiply, transpose, reshape, regex, rand, Complex - Will Not Transpile: file I/O, networking, threading, metaprogramming, ccall Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 430539c commit 8e32304

1 file changed

Lines changed: 113 additions & 87 deletions

File tree

README.md

Lines changed: 113 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,112 @@ println(result.js)
2424

2525
Includes a self-hosted [browser playground](https://grouptherapyorg.github.io/JavaScriptTarget.jl/) — type Julia, run it as JS, no server needed.
2626

27-
## What Transpiles
28-
29-
| Julia | JavaScript |
30-
|---|---|
31-
| `Int32`, `Int64` | `number` (integer ops use `\|0`) |
32-
| `Float64` | `number` |
33-
| `String` | `string` |
34-
| `Bool` | `boolean` |
35-
| `Vector{T}` | `Array` (`push!`, `getindex`, `length`) |
36-
| `Dict{K,V}` | `Map` |
37-
| `struct` | ES6 `class` |
38-
| `sin`, `cos`, `sqrt`, ... | `Math.sin`, `Math.cos`, `Math.sqrt` |
39-
| `println` | `console.log` |
40-
| `sin.(x)` | `x.map(v => Math.sin(v))` |
41-
| `x .* freq` | `x.map(v => v * freq)` |
42-
| `for i in 1:n` | `while` loop |
27+
## Transpilation Coverage
28+
29+
### Fully Transpiled
30+
31+
| Category | Julia | JavaScript |
32+
|----------|-------|-----------|
33+
| **Types** | `Int32`, `Int64` | `number` (integer ops use `\|0`) |
34+
| | `Float64` | `number` |
35+
| | `String` | `string` |
36+
| | `Bool` | `boolean` |
37+
| | `Nothing` | `null` |
38+
| | `struct` / `mutable struct` | ES6 `class` |
39+
| | `Tuple` | `Array` |
40+
| **1D Arrays** | `Vector{T}`, `T[]` | `Array` |
41+
| | `push!`, `pop!`, `append!` | `.push()`, `.pop()`, `.push(...other)` |
42+
| | `length`, `getindex`, `setindex!` | `.length`, `[i-1]`, `[i-1] = v` |
43+
| | `copy`, `reverse`, `empty!` | `.slice()`, `[...a].reverse()`, `.length=0` |
44+
| | `deleteat!` | `.splice(i-1, 1)` |
45+
| | `sort` (with `by=`, `rev=`) | `.slice().sort(compareFn)` |
46+
| | `in` / `` | `.includes()` |
47+
| **ND Arrays** | `zeros(m,n)`, `ones(m,n)`, `fill(v,m,n)` | Flat `Array` + `_size` metadata (column-major) |
48+
| | `A[i,j]`, `A[i,j,k]` | Column-major stride: `A[(j-1)*m+(i-1)]` |
49+
| | `A[i,j] = val` | Same stride for write |
50+
| | `size(A)`, `size(A,d)` | `A._size`, `A._size[d-1]` |
51+
| | `length(A)` (total elements) | `A.length` |
52+
| **Higher-Order** | `map(f, arr)` | `arr.map(f)` |
53+
| | `filter(f, arr)` | `arr.filter(f)` |
54+
| | `any(f, arr)`, `all(f, arr)` | `arr.some(f)`, `arr.every(f)` |
55+
| | `findfirst(f, arr)` | `arr.findIndex(f)+1` |
56+
| | `reduce(f, arr)` | `arr.reduce(f)` |
57+
| **Strings** | `lowercase`, `uppercase` | `.toLowerCase()`, `.toUpperCase()` |
58+
| | `contains`, `occursin` | `.includes()` |
59+
| | `startswith`, `endswith` | `.startsWith()`, `.endsWith()` |
60+
| | `split`, `join` | `.split()`, `.join()` |
61+
| | `replace` | `.replaceAll()` |
62+
| | `strip`, `lstrip`, `rstrip` | `.trim()`, `.trimStart()`, `.trimEnd()` |
63+
| | `repeat`, `chop`, `chomp`, `reverse` | `.repeat()`, `.slice(0,-1)`, etc. |
64+
| | `string(...)` concatenation | Template literals |
65+
| **Math** | `sin`, `cos`, `tan`, `asin`, `acos`, `atan` | `Math.sin`, `Math.cos`, etc. |
66+
| | `exp`, `log`, `log2`, `log10` | `Math.exp`, `Math.log`, etc. |
67+
| | `sqrt`, `abs`, `min`, `max` | `Math.sqrt`, `Math.abs`, etc. |
68+
| | `floor`, `ceil`, `round`, `trunc`, `sign` | `Math.floor`, etc. |
69+
| | `hypot`, `atan2` | `Math.hypot`, `Math.atan2` |
70+
| **Arithmetic** | `+`, `-`, `*`, `/`, `%` | Same (integers use `\|0`) |
71+
| | `<`, `<=`, `>`, `>=`, `==`, `!=` | `<`, `<=`, `>`, `>=`, `===`, `!==` |
72+
| | `&`, `\|`, ``, `<<`, `>>`, `>>>` | Bitwise ops |
73+
| **Broadcasting** | `sin.(x)` | `x.map(v => Math.sin(v))` |
74+
| | `x .* y` (scalar-array) | `x.map(v => v * y)` |
75+
| | `x .* y` (array-array) | `x.map((v,i) => v * y[i])` |
76+
| | Nested: `sin.(x .* f)` | Chained `.map()` |
77+
| **Control Flow** | `if`/`elseif`/`else` | `if`/`else if`/`else` |
78+
| | `for i in 1:n` (single + nested) | `while(true)` loops |
79+
| | `while` loops | `while` loops |
80+
| | Short-circuit `&&`, `\|\|` | Same |
81+
| | Ternary `a ? b : c` | Same |
82+
| **Functions** | Named functions | `function name() {}` |
83+
| | Closures (captured variables) | JS closures |
84+
| | `@noinline` functions | Preserved as `:invoke` in IR |
85+
| **Structs** | `struct` fields + constructor | `class` with `constructor` |
86+
| | Field access `obj.field` | `obj.field` |
87+
| | `setfield!` (mutable) | `obj.field = val` |
88+
| **Collections** | `Dict{K,V}` | `Map` |
89+
| | `Set{T}` | `Set` |
90+
| | Dict: `setindex!`, `getindex`, `delete!`, `get`, `haskey` | `.set()`, `.get()`, `.delete()`, `.has()` |
91+
| | Set: `push!`, `delete!`, `in` | `.add()`, `.delete()`, `.has()` |
92+
| **IO** | `println(...)` | `console.log(...)` |
93+
| | `print(...)` | `console.log(...)` (no newline) |
94+
| **Parsing** | `parse(Int, s)` | `parseInt(s, 10)` |
95+
| | `parse(Float64, s)` | `parseFloat(s)` |
96+
| **Construction** | `zeros(n)`, `ones(n)`, `fill(v,n)` | `new Array(n).fill(...)` |
97+
| | `zeros(m,n)`, `ones(m,n)`, `fill(v,m,n)` | `jl_ndarray(val, [m,n])` |
98+
| **Other** | `isempty(x)` | `x.length === 0` |
99+
| | `convert(T, x)` | `x` (identity) |
100+
| | `Float64(x)`, `Int(x)` | `+(x)`, `(x)\|0` |
101+
| | `typeof`, `isa` | `typeof`, `instanceof` |
102+
| | `try`/`catch` | `try`/`catch` |
103+
| | `js("raw code")` | Raw JS emission |
104+
| | Package registry | Custom JS for registered packages |
105+
106+
### Not Yet Transpiled (planned)
107+
108+
| Category | Julia | Status |
109+
|----------|-------|--------|
110+
| **Linear Algebra** | `A * B` (matrix multiply) | Use manual loops for now |
111+
| | `transpose(A)`, `A'` | Planned |
112+
| | `det`, `inv`, `eigen` | Planned (via package registry) |
113+
| **Array Ops** | `reshape(A, m, n)` | Planned |
114+
| | `hcat`, `vcat` | Planned |
115+
| | `view`, `@view` | Planned |
116+
| | `enumerate` (standalone) | Works in for-loop context |
117+
| **Strings** | Regex matching | Planned |
118+
| | Unicode operations | Basic support via runtime helpers |
119+
| **Numbers** | `rand()`, `randn()` | Planned (`Math.random()`) |
120+
| | `Complex{T}` | Planned |
121+
| | `Rational{T}` | Not planned |
122+
123+
### Will Not Transpile
124+
125+
| Category | Reason |
126+
|----------|--------|
127+
| File I/O (`open`, `read`, `write`) | No filesystem in browser |
128+
| Networking (`HTTP`, sockets) | Browser security model |
129+
| Multi-threading (`Threads`, `@spawn`) | JS is single-threaded (use Web Workers separately) |
130+
| Metaprogramming (`@eval`, `eval`, `Meta.parse`) | No Julia runtime in browser |
131+
| C interop (`ccall`, `@ccall`) | No native binaries |
132+
| Package loading (`using`, `import` at runtime) | Static transpilation only |
43133

44134
## Arrays and Broadcasting
45135

@@ -58,90 +148,26 @@ end
58148
result = JST.compile(make_data, (Int, Float64); optimize=false)
59149
```
60150

61-
Produces:
62-
```javascript
63-
function make_data(n, freq) {
64-
x = [];
65-
// ... for loop with x.push() ...
66-
y = x.map(_b => _b * freq).map(_b => Math.sin(_b));
67-
return [x, y];
68-
}
69-
```
70-
71-
JST auto-detects when `optimize=false` is needed (when the optimized IR contains Julia's internal memory management) and falls back automatically when used via [Therapy.jl](https://github.com/GroupTherapyOrg/Therapy.jl).
151+
JST auto-detects when `optimize=false` is needed and falls back automatically when used via [Therapy.jl](https://github.com/GroupTherapyOrg/Therapy.jl).
72152

73153
## Package Compilation Registry
74154

75-
JST can transpile calls to registered Julia packages into their JavaScript equivalents — instead of transpiling the package's internal implementation.
76-
77-
### Registering a Package
155+
Register custom JS output for any Julia package function:
78156

79157
```julia
80158
import JavaScriptTarget as JST
81159

82-
# Register a function: (module, function_name) → JS compiler
83-
JST.register_package_compilation!(MyPlotLib, :scatter) do ctx, kwargs, pos_args
84-
# kwargs: Dict{Symbol, String} — compiled keyword argument values
85-
# pos_args: Vector{String} — compiled positional argument values
86-
# Return a JS code string
87-
JST.build_js_object_from_kwargs(kwargs; type="scatter")
88-
end
89-
```
90-
91-
The compiler function receives:
92-
- `ctx` — compilation context
93-
- `kwargs` — keyword arguments as `Dict{Symbol, String}` (kwarg name → compiled JS expression)
94-
- `pos_args` — positional arguments as `Vector{String}` (compiled JS expressions)
95-
96-
### Built-in: Plotly
97-
98-
JST ships with Plotly mappings. Register them for any module that exports `scatter`, `Layout`, etc.:
99-
100-
```julia
101-
# Register your module's Plotly-compatible functions
102-
JST.register_plotly_compilations!(MyModule)
103-
```
104-
105-
This maps:
106-
107-
| Julia | JavaScript |
108-
|---|---|
109-
| `scatter(x=x, y=y, mode="lines")` | `{"type":"scatter", "x":x, "y":y, "mode":"lines"}` |
110-
| `Layout(title="Test", xaxis=...)` | `{"title":"Test", "xaxis":...}` |
111-
| `plotly("div-id", traces, layout)` | `Plotly.newPlot(el, traces, layout)` / `Plotly.react(...)` |
112-
113-
### Adding Your Own Package
114-
115-
```julia
116-
module MyPackage
117-
import JavaScriptTarget as JST
118-
119-
# Define Julia functions
120-
my_chart(; data, options=nothing) = Dict("data" => data, "options" => options)
121-
122-
# Register compilation
123-
function __init__()
124-
JST.register_package_compilation!(@__MODULE__, :my_chart) do ctx, kwargs, pos_args
125-
JST.build_js_object_from_kwargs(kwargs)
126-
end
127-
end
160+
JST.register_package_compilation!(MyPkg, :my_func) do ctx, kwargs, pos_args
161+
# Return JS code string
162+
JST.build_js_object_from_kwargs(kwargs)
128163
end
129164
```
130165

131-
When JST encounters `MyPackage.my_chart(data=x, options=cfg)` in compiled code, it emits `{"data": x, "options": cfg}` instead of trying to compile the function's Julia implementation.
132-
133-
### How It Works
134-
135-
1. Julia's `code_typed` lowers keyword calls to `Core.kwcall(NamedTuple{names}(values), func)`
136-
2. JST extracts the kwarg names from the NamedTuple type parameters
137-
3. The registered compiler function receives the compiled kwarg values
138-
4. The compiler emits the JS equivalent (object literals, function calls, etc.)
139-
140-
The registry is checked for both keyword calls (`Core.kwcall`) and positional calls. Functions are matched by `(parentmodule(fn), nameof(fn))`.
166+
Built-in Plotly support: `JST.register_plotly_compilations!(MyModule)`
141167

142168
## `js()` Escape Hatch
143169

144-
For direct browser API access, `js()` emits raw JavaScript:
170+
For browser APIs that can't be expressed in Julia:
145171

146172
```julia
147173
js("document.title = 'Hello'")

0 commit comments

Comments
 (0)