-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDeno.affine
More file actions
184 lines (141 loc) · 8.72 KB
/
Copy pathDeno.affine
File metadata and controls
184 lines (141 loc) · 8.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
//
// Deno.affine — issue #122 host bindings for the Deno-ESM backend.
//
// Unlike stdlib/Vscode.affine (issue #35), these externs are NOT a
// wasm-FFI surface with an Int-handle/readString contract. The
// `--deno-esm` backend (lib/codegen_deno.ml) is a *direct* AST → ES
// module transpiler with no wasm boundary, so each `extern fn` below is
// lowered, at compile time, straight to its host expression:
//
// writeTextFile(p, c) -> Deno.writeTextFileSync(p, c)
// jsonParse(s) -> JSON.parse(s)
// dateNow() -> Date.now()
// ...
//
// The lowering table lives in lib/codegen_deno.ml (`deno_builtins`);
// the non-trivial leaves are emitted inlined into every module's
// prelude so the output is genuinely drop-in (no runtime adapter, no
// extra package to resolve). packages/affine-deno/mod.js mirrors the
// same surface as a standalone ESM module for `deno test`.
//
// All FS operations are synchronous (`Deno.*Sync`). `await` on a
// synchronously-returned value is valid JS, so an async-shaped consumer
// (e.g. hyperpolymath/ubicity) keeps working without an async-extern
// ABI (issue #103 — documented future work, not required here).
//
// Names here MUST match the `deno_builtins` table keys exactly; an
// unmatched extern silently falls through to a same-named host symbol.
module Deno;
// ── Opaque host value types ────────────────────────────────────────
//
// `Json` is any structured JS value (object/array/string/number/bool/
// null) crossing the boundary opaquely — the AffineScript side never
// inspects it, it only routes it between JSON.* and the host. `Bytes`
// is a Uint8Array; `WasmExports` is an instantiated module's exports.
pub extern type Json;
pub extern type Bytes;
pub extern type WasmExports;
// ── Filesystem (synchronous) ───────────────────────────────────────
/// `Deno.writeTextFileSync(path, content)`. Returns 0.
pub extern fn writeTextFile(path: String, content: String) -> Int;
/// `Deno.readTextFileSync(path)`. Throws on missing file — pair with
/// `isNotFound` in a `try`/`catch` for the not-found-is-null pattern.
pub extern fn readTextFile(path: String) -> String;
/// `Deno.readFileSync(path)` — raw bytes (for wasm modules).
pub extern fn readFileBytes(path: String) -> Bytes;
/// `Deno.removeSync(path)`. Throws if absent (catch + `isNotFound`).
pub extern fn removePath(path: String) -> Int;
/// `Deno.mkdirSync(path, { recursive: true })`.
pub extern fn mkdirRecursive(path: String) -> Int;
/// mkdir -p that swallows AlreadyExists (idempotent ensure-directory).
pub extern fn ensureDir(path: String) -> Int;
/// Names of the *file* entries in `path` (skips sub-directories).
pub extern fn readDirNames(path: String) -> [String];
/// `Deno.statSync(path).size` in bytes.
pub extern fn statSize(path: String) -> Int;
/// Recursive walk under `root` — every file path beneath it, depth-first.
/// Mirrors `std/fs/walk` for the common case (no glob filter; callers
/// filter by extension). Throws on a missing root via `Deno.readDirSync`.
pub extern fn walkRecursive(root: String) -> [String];
// ── Path ───────────────────────────────────────────────────────────
/// Single-segment join with a `/` separator (idempotent on a trailing
/// slash). Sufficient for the storage-layout use-case.
pub extern fn pathJoin(a: String, b: String) -> String;
// ── Error classification ───────────────────────────────────────────
/// `e instanceof Deno.errors.NotFound` — the only error class the
/// storage layer special-cases (missing file/dir => null/empty).
pub extern fn isNotFound(e: Json) -> Bool;
// ── JSON ───────────────────────────────────────────────────────────
pub extern fn jsonStringify(v: Json) -> String;
/// `JSON.stringify(v, null, 2)` — the on-disk pretty form.
pub extern fn jsonStringifyPretty(v: Json) -> String;
pub extern fn jsonParse(s: String) -> Json;
/// JS `null` as an opaque Json (the not-found / absent sentinel).
pub extern fn jsonNull() -> Json;
/// Opaque field/index read: `value[key]`. The boundary primitive for
/// treating an arbitrary host JS value as data without the AffineScript
/// side modelling its shape (e.g. `experience.id`).
pub extern fn jsonGet(value: Json, key: String) -> Json;
pub extern fn jsonGetStr(value: Json, key: String) -> String;
/// Nullish default — `x ?? d`. Preserves a JS default parameter when
/// the caller omits the argument.
pub extern fn orDefault(x: String, d: String) -> String;
/// Kilobyte display string: `(bytes / 1024).toFixed(2)`. Runtime number
/// formatting is an honest host primitive (cf. Rust `format!`).
pub extern fn kbString(bytes: Int) -> String;
// ── Misc host ──────────────────────────────────────────────────────
/// `Date.now()` — epoch millis (used for timestamped report names).
pub extern fn dateNow() -> Int;
/// `new Date().toISOString()` — UTC ISO-8601 timestamp string
/// (e.g. `"2026-05-30T12:34:56.789Z"`). Distinct from `dateNow()` which
/// returns epoch millis as `Int`.
pub extern fn dateNowIso() -> String;
// ── CLI ────────────────────────────────────────────────────────────
/// `Deno.args` — command-line arguments (excludes argv[0]).
pub extern fn args() -> [String];
/// `Deno.exit(code)` — terminate the process with `code`. Never returns;
/// the `Int` return type is for type-level compatibility with `if/else`
/// arms that flow through `exit` in their non-returning branch.
pub extern fn exit(code: Int) -> Int;
// ── Diagnostics ────────────────────────────────────────────────────
/// `console.error(s)` — write to stderr. (Use `print`/`println` for
/// stdout.) Returns 0 for chaining.
pub extern fn consoleError(s: String) -> Int;
// ── Regex ──────────────────────────────────────────────────────────
/// `new RegExp(pat).test(s)` — true iff `s` matches the JS regex source
/// `pat`. Minimal regex surface; for extraction or replace, add a
/// specialised extern. Invalid `pat` throws at call time.
pub extern fn regexMatch(s: String, pat: String) -> Bool;
/// `(Number(bytes) / 1024).toFixed(2)` — kilobyte display string.
pub extern fn numToFixed2(bytes: Int) -> String;
pub extern fn endsWith(s: String, suffix: String) -> Bool;
/// `s` with a trailing `suffix` removed (no-op if absent).
pub extern fn stripSuffix(s: String, suffix: String) -> String;
// ── WebAssembly (synchronous instantiate) ──────────────────────────
/// `new WebAssembly.Instance(new WebAssembly.Module(bytes)).exports`.
pub extern fn wasmInstance(bytes: Bytes) -> WasmExports;
/// `exports[name](...args)` — invoke a named export with a list of
/// Float arguments. WebAssembly's i32/i64/f32/f64 scalar types all
/// coerce to JS Number, so a single Float-typed surface covers the
/// common case (multi-value / void returns are out of scope here —
/// add a specialised extern when needed). Caller is responsible for
/// the export existing and having a compatible arity; absent exports
/// throw `TypeError: ... is not a function` at the host boundary.
///
/// Example:
///
/// use Deno::{Bytes, WasmExports, wasmInstance, wasmCall};
///
/// pub fn addViaWasm(bytes: Bytes, a: Float, b: Float) -> Float = {
/// let exports = wasmInstance(bytes);
/// wasmCall(exports, "add", [a, b])
/// };
pub extern fn wasmCall(exports: WasmExports, name: String, args: [Float]) -> Float;
// ── Array helper ───────────────────────────────────────────────────
//
// AffineScript has no mutable-array push primitive in this subset;
// this fluent helper appends and returns the array so accumulation
// reads functionally: `acc = arrayPush(acc, x)`.
pub extern fn arrayPush(arr: [Json], v: Json) -> [Json];