edict-lang 1.2.0 → 1.5.0
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/dist/ast/nodes.d.ts +3 -3
- package/dist/ast/nodes.d.ts.map +1 -1
- package/dist/ast/nodes.js +1 -0
- package/dist/ast/nodes.js.map +1 -1
- package/dist/ast/type-constants.d.ts +12 -0
- package/dist/ast/type-constants.d.ts.map +1 -0
- package/dist/ast/type-constants.js +16 -0
- package/dist/ast/type-constants.js.map +1 -0
- package/dist/ast/types.d.ts +1 -1
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/builtins/builtin-enums.d.ts +12 -0
- package/dist/builtins/builtin-enums.d.ts.map +1 -0
- package/dist/builtins/builtin-enums.js +45 -0
- package/dist/builtins/builtin-enums.js.map +1 -0
- package/dist/builtins/builtins.d.ts +24 -0
- package/dist/builtins/builtins.d.ts.map +1 -0
- package/dist/builtins/builtins.js +691 -0
- package/dist/builtins/builtins.js.map +1 -0
- package/dist/checker/check.d.ts.map +1 -1
- package/dist/checker/check.js +81 -38
- package/dist/checker/check.js.map +1 -1
- package/dist/checker/type-env.d.ts +2 -0
- package/dist/checker/type-env.d.ts.map +1 -1
- package/dist/checker/type-env.js +9 -0
- package/dist/checker/type-env.js.map +1 -1
- package/dist/codegen/browser-host-adapter.d.ts +29 -0
- package/dist/codegen/browser-host-adapter.d.ts.map +1 -0
- package/dist/codegen/browser-host-adapter.js +51 -0
- package/dist/codegen/browser-host-adapter.js.map +1 -0
- package/dist/codegen/builtins.d.ts +2 -26
- package/dist/codegen/builtins.d.ts.map +1 -1
- package/dist/codegen/builtins.js +2 -341
- package/dist/codegen/builtins.js.map +1 -1
- package/dist/codegen/closures.d.ts +17 -0
- package/dist/codegen/closures.d.ts.map +1 -0
- package/dist/codegen/closures.js +140 -0
- package/dist/codegen/closures.js.map +1 -0
- package/dist/codegen/codegen.d.ts +11 -30
- package/dist/codegen/codegen.d.ts.map +1 -1
- package/dist/codegen/codegen.js +154 -982
- package/dist/codegen/codegen.js.map +1 -1
- package/dist/codegen/collect-strings.d.ts +4 -0
- package/dist/codegen/collect-strings.d.ts.map +1 -0
- package/dist/codegen/collect-strings.js +76 -0
- package/dist/codegen/collect-strings.js.map +1 -0
- package/dist/codegen/compile-calls.d.ts +10 -0
- package/dist/codegen/compile-calls.d.ts.map +1 -0
- package/dist/codegen/compile-calls.js +344 -0
- package/dist/codegen/compile-calls.js.map +1 -0
- package/dist/codegen/compile-data.d.ts +22 -0
- package/dist/codegen/compile-data.d.ts.map +1 -0
- package/dist/codegen/compile-data.js +243 -0
- package/dist/codegen/compile-data.js.map +1 -0
- package/dist/codegen/compile-match.d.ts +7 -0
- package/dist/codegen/compile-match.d.ts.map +1 -0
- package/dist/codegen/compile-match.js +195 -0
- package/dist/codegen/compile-match.js.map +1 -0
- package/dist/codegen/compile-scalars.d.ts +25 -0
- package/dist/codegen/compile-scalars.d.ts.map +1 -0
- package/dist/codegen/compile-scalars.js +210 -0
- package/dist/codegen/compile-scalars.js.map +1 -0
- package/dist/codegen/hof-generators.d.ts +39 -0
- package/dist/codegen/hof-generators.d.ts.map +1 -0
- package/dist/codegen/hof-generators.js +336 -0
- package/dist/codegen/hof-generators.js.map +1 -0
- package/dist/codegen/host-adapter.d.ts +44 -0
- package/dist/codegen/host-adapter.d.ts.map +1 -0
- package/dist/codegen/host-adapter.js +9 -0
- package/dist/codegen/host-adapter.js.map +1 -0
- package/dist/codegen/host-functions.d.ts +35 -0
- package/dist/codegen/host-functions.d.ts.map +1 -0
- package/dist/codegen/host-functions.js +680 -0
- package/dist/codegen/host-functions.js.map +1 -0
- package/dist/codegen/imports.d.ts +12 -0
- package/dist/codegen/imports.d.ts.map +1 -0
- package/dist/codegen/imports.js +162 -0
- package/dist/codegen/imports.js.map +1 -0
- package/dist/codegen/node-host-adapter.d.ts +35 -0
- package/dist/codegen/node-host-adapter.d.ts.map +1 -0
- package/dist/codegen/node-host-adapter.js +155 -0
- package/dist/codegen/node-host-adapter.js.map +1 -0
- package/dist/codegen/runner.d.ts +36 -2
- package/dist/codegen/runner.d.ts.map +1 -1
- package/dist/codegen/runner.js +146 -271
- package/dist/codegen/runner.js.map +1 -1
- package/dist/codegen/types.d.ts +91 -0
- package/dist/codegen/types.d.ts.map +1 -0
- package/dist/codegen/types.js +63 -0
- package/dist/codegen/types.js.map +1 -0
- package/dist/compact/expand.d.ts +25 -0
- package/dist/compact/expand.d.ts.map +1 -0
- package/dist/compact/expand.js +198 -0
- package/dist/compact/expand.js.map +1 -0
- package/dist/compile.d.ts +2 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +1 -1
- package/dist/compile.js.map +1 -1
- package/dist/contracts/translate.js.map +1 -1
- package/dist/effects/call-graph.d.ts.map +1 -1
- package/dist/effects/call-graph.js +6 -2
- package/dist/effects/call-graph.js.map +1 -1
- package/dist/errors/error-catalog.d.ts +1 -1
- package/dist/errors/error-catalog.d.ts.map +1 -1
- package/dist/errors/error-catalog.js +119 -0
- package/dist/errors/error-catalog.js.map +1 -1
- package/dist/errors/structured-errors.d.ts +6 -1
- package/dist/errors/structured-errors.d.ts.map +1 -1
- package/dist/errors/structured-errors.js +3 -0
- package/dist/errors/structured-errors.js.map +1 -1
- package/dist/index.d.ts +17 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -10
- package/dist/index.js.map +1 -1
- package/dist/lint/lint.d.ts +9 -0
- package/dist/lint/lint.d.ts.map +1 -0
- package/dist/lint/lint.js +354 -0
- package/dist/lint/lint.js.map +1 -0
- package/dist/lint/warnings.d.ts +54 -0
- package/dist/lint/warnings.d.ts.map +1 -0
- package/dist/lint/warnings.js +39 -0
- package/dist/lint/warnings.js.map +1 -0
- package/dist/mcp/create-server.d.ts.map +1 -1
- package/dist/mcp/create-server.js +66 -5
- package/dist/mcp/create-server.js.map +1 -1
- package/dist/mcp/handlers.d.ts +18 -4
- package/dist/mcp/handlers.d.ts.map +1 -1
- package/dist/mcp/handlers.js +54 -12
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/mcp/prompts.d.ts +17 -0
- package/dist/mcp/prompts.d.ts.map +1 -0
- package/dist/mcp/prompts.js +181 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/server.js +1 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/resolver/resolve.d.ts.map +1 -1
- package/dist/resolver/resolve.js +22 -8
- package/dist/resolver/resolve.js.map +1 -1
- package/dist/validator/node-validators.d.ts.map +1 -1
- package/dist/validator/node-validators.js +34 -5
- package/dist/validator/node-validators.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Host Function Imports — WASM ↔ Host bridge for Edict runtime
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// All host functions that WASM modules import are defined here.
|
|
5
|
+
// The runner calls `createHostImports()` and passes the result as the
|
|
6
|
+
// import object to `WebAssembly.instantiate()`.
|
|
7
|
+
//
|
|
8
|
+
// Platform-agnostic groups (string, math, array, etc.) use Web Standard APIs.
|
|
9
|
+
// Platform-specific groups (crypto, HTTP, IO) delegate to an EdictHostAdapter.
|
|
10
|
+
import { NodeHostAdapter } from "./node-host-adapter.js";
|
|
11
|
+
/** Typed error thrown when a host-side heap allocation exceeds WASM memory bounds. */
|
|
12
|
+
export class EdictOomError extends Error {
|
|
13
|
+
heapUsed;
|
|
14
|
+
heapLimit;
|
|
15
|
+
constructor(heapUsed, heapLimit) {
|
|
16
|
+
super("edict_oom: heap exhausted");
|
|
17
|
+
this.heapUsed = heapUsed;
|
|
18
|
+
this.heapLimit = heapLimit;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Memory helpers
|
|
23
|
+
// =============================================================================
|
|
24
|
+
function getMemoryBuffer(state) {
|
|
25
|
+
return state.instance.exports.memory.buffer;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Centralized heap allocator with bounds checking.
|
|
29
|
+
* Allocates `size` bytes (8-byte aligned) from the bump allocator,
|
|
30
|
+
* throwing EdictOomError if the allocation would exceed WASM memory.
|
|
31
|
+
*/
|
|
32
|
+
function allocateHeap(state, size) {
|
|
33
|
+
const getHeapPtr = state.instance.exports.__get_heap_ptr;
|
|
34
|
+
const setHeapPtr = state.instance.exports.__set_heap_ptr;
|
|
35
|
+
const ptr = getHeapPtr();
|
|
36
|
+
const aligned = Math.ceil(size / 8) * 8;
|
|
37
|
+
const newPtr = ptr + aligned;
|
|
38
|
+
const memorySize = getMemoryBuffer(state).byteLength;
|
|
39
|
+
if (newPtr > memorySize) {
|
|
40
|
+
throw new EdictOomError(ptr, memorySize);
|
|
41
|
+
}
|
|
42
|
+
setHeapPtr(newPtr);
|
|
43
|
+
return ptr;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Write a string result into WASM memory at __heap_ptr,
|
|
47
|
+
* advance __heap_ptr (8-byte aligned), set __str_ret_len, and return ptr.
|
|
48
|
+
*/
|
|
49
|
+
function writeStringResult(state, str, encoder) {
|
|
50
|
+
const encoded = encoder.encode(str);
|
|
51
|
+
const resultPtr = allocateHeap(state, encoded.length);
|
|
52
|
+
const dest = new Uint8Array(getMemoryBuffer(state), resultPtr, encoded.length);
|
|
53
|
+
dest.set(encoded);
|
|
54
|
+
const setStrRetLen = state.instance.exports.__set_str_ret_len;
|
|
55
|
+
setStrRetLen(encoded.length);
|
|
56
|
+
return resultPtr;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Allocate a new array on the WASM heap: [length: i32][elem0: i32]...
|
|
60
|
+
* Advances __heap_ptr (8-byte aligned) and returns the new array pointer.
|
|
61
|
+
*/
|
|
62
|
+
function writeArrayResult(state, elements) {
|
|
63
|
+
const totalSize = 4 + elements.length * 4; // header + elements
|
|
64
|
+
const resultPtr = allocateHeap(state, totalSize);
|
|
65
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
66
|
+
view.setInt32(resultPtr, elements.length, true); // write length
|
|
67
|
+
for (let i = 0; i < elements.length; i++) {
|
|
68
|
+
view.setInt32(resultPtr + 4 + i * 4, elements[i], true);
|
|
69
|
+
}
|
|
70
|
+
return resultPtr;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Allocate a Result value on the WASM heap: [tag: i32][pad(4)][value: i32][pad(4)]
|
|
74
|
+
* tag=0 means Ok, tag=1 means Err. Total size = 16 bytes (matches enum layout).
|
|
75
|
+
* Returns the pointer to the Result pair.
|
|
76
|
+
*/
|
|
77
|
+
function writeResultValue(state, tag, value) {
|
|
78
|
+
const ptr = allocateHeap(state, 16);
|
|
79
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
80
|
+
view.setInt32(ptr, tag, true); // tag at offset 0
|
|
81
|
+
view.setInt32(ptr + 8, value, true); // value at offset 8 (matches EnumVariantLayout)
|
|
82
|
+
return ptr;
|
|
83
|
+
}
|
|
84
|
+
// =============================================================================
|
|
85
|
+
// Core host functions (print, string_replace)
|
|
86
|
+
// =============================================================================
|
|
87
|
+
function createCoreImports(state) {
|
|
88
|
+
return {
|
|
89
|
+
print: (ptr, len) => {
|
|
90
|
+
const bytes = new Uint8Array(getMemoryBuffer(state), ptr, len);
|
|
91
|
+
const text = new TextDecoder().decode(bytes);
|
|
92
|
+
state.outputParts.push(text);
|
|
93
|
+
return ptr;
|
|
94
|
+
},
|
|
95
|
+
string_replace: (hayPtr, hayLen, needlePtr, needleLen, replPtr, replLen) => {
|
|
96
|
+
const memoryBuffer = getMemoryBuffer(state);
|
|
97
|
+
const decoder = new TextDecoder();
|
|
98
|
+
const encoder = new TextEncoder();
|
|
99
|
+
const haystack = decoder.decode(new Uint8Array(memoryBuffer, hayPtr, hayLen));
|
|
100
|
+
const needle = decoder.decode(new Uint8Array(memoryBuffer, needlePtr, needleLen));
|
|
101
|
+
const replacement = decoder.decode(new Uint8Array(memoryBuffer, replPtr, replLen));
|
|
102
|
+
return writeStringResult(state, haystack.replaceAll(needle, replacement), encoder);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// =============================================================================
|
|
107
|
+
// String builtins
|
|
108
|
+
// =============================================================================
|
|
109
|
+
function createStringImports(state) {
|
|
110
|
+
return {
|
|
111
|
+
string_length: (ptr, len) => {
|
|
112
|
+
const str = new TextDecoder().decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
113
|
+
return str.length;
|
|
114
|
+
},
|
|
115
|
+
substring: (ptr, len, start, end) => {
|
|
116
|
+
const str = new TextDecoder().decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
117
|
+
return writeStringResult(state, str.substring(start, end), new TextEncoder());
|
|
118
|
+
},
|
|
119
|
+
string_concat: (aPtr, aLen, bPtr, bLen) => {
|
|
120
|
+
const decoder = new TextDecoder();
|
|
121
|
+
const buf = getMemoryBuffer(state);
|
|
122
|
+
const a = decoder.decode(new Uint8Array(buf, aPtr, aLen));
|
|
123
|
+
const b = decoder.decode(new Uint8Array(buf, bPtr, bLen));
|
|
124
|
+
return writeStringResult(state, a + b, new TextEncoder());
|
|
125
|
+
},
|
|
126
|
+
string_indexOf: (hayPtr, hayLen, needlePtr, needleLen) => {
|
|
127
|
+
const decoder = new TextDecoder();
|
|
128
|
+
const buf = getMemoryBuffer(state);
|
|
129
|
+
const haystack = decoder.decode(new Uint8Array(buf, hayPtr, hayLen));
|
|
130
|
+
const needle = decoder.decode(new Uint8Array(buf, needlePtr, needleLen));
|
|
131
|
+
return haystack.indexOf(needle);
|
|
132
|
+
},
|
|
133
|
+
toUpperCase: (ptr, len) => {
|
|
134
|
+
const str = new TextDecoder().decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
135
|
+
return writeStringResult(state, str.toUpperCase(), new TextEncoder());
|
|
136
|
+
},
|
|
137
|
+
toLowerCase: (ptr, len) => {
|
|
138
|
+
const str = new TextDecoder().decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
139
|
+
return writeStringResult(state, str.toLowerCase(), new TextEncoder());
|
|
140
|
+
},
|
|
141
|
+
string_trim: (ptr, len) => {
|
|
142
|
+
const str = new TextDecoder().decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
143
|
+
return writeStringResult(state, str.trim(), new TextEncoder());
|
|
144
|
+
},
|
|
145
|
+
string_startsWith: (strPtr, strLen, prefixPtr, prefixLen) => {
|
|
146
|
+
const decoder = new TextDecoder();
|
|
147
|
+
const buf = getMemoryBuffer(state);
|
|
148
|
+
const str = decoder.decode(new Uint8Array(buf, strPtr, strLen));
|
|
149
|
+
const prefix = decoder.decode(new Uint8Array(buf, prefixPtr, prefixLen));
|
|
150
|
+
return str.startsWith(prefix) ? 1 : 0;
|
|
151
|
+
},
|
|
152
|
+
string_endsWith: (strPtr, strLen, suffixPtr, suffixLen) => {
|
|
153
|
+
const decoder = new TextDecoder();
|
|
154
|
+
const buf = getMemoryBuffer(state);
|
|
155
|
+
const str = decoder.decode(new Uint8Array(buf, strPtr, strLen));
|
|
156
|
+
const suffix = decoder.decode(new Uint8Array(buf, suffixPtr, suffixLen));
|
|
157
|
+
return str.endsWith(suffix) ? 1 : 0;
|
|
158
|
+
},
|
|
159
|
+
string_contains: (hayPtr, hayLen, needlePtr, needleLen) => {
|
|
160
|
+
const decoder = new TextDecoder();
|
|
161
|
+
const buf = getMemoryBuffer(state);
|
|
162
|
+
const haystack = decoder.decode(new Uint8Array(buf, hayPtr, hayLen));
|
|
163
|
+
const needle = decoder.decode(new Uint8Array(buf, needlePtr, needleLen));
|
|
164
|
+
return haystack.includes(needle) ? 1 : 0;
|
|
165
|
+
},
|
|
166
|
+
string_repeat: (ptr, len, count) => {
|
|
167
|
+
const str = new TextDecoder().decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
168
|
+
return writeStringResult(state, str.repeat(count), new TextEncoder());
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
// =============================================================================
|
|
173
|
+
// Math builtins
|
|
174
|
+
// =============================================================================
|
|
175
|
+
function createMathImports() {
|
|
176
|
+
return {
|
|
177
|
+
abs: (x) => Math.abs(x),
|
|
178
|
+
min: (a, b) => Math.min(a, b),
|
|
179
|
+
max: (a, b) => Math.max(a, b),
|
|
180
|
+
pow: (base, exp) => (Math.pow(base, exp) | 0),
|
|
181
|
+
sqrt: (x) => Math.sqrt(x),
|
|
182
|
+
floor: (x) => (Math.floor(x) | 0),
|
|
183
|
+
ceil: (x) => (Math.ceil(x) | 0),
|
|
184
|
+
round: (x) => (Math.round(x) | 0),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// =============================================================================
|
|
188
|
+
// Type conversion builtins
|
|
189
|
+
// =============================================================================
|
|
190
|
+
function createTypeConversionImports(state) {
|
|
191
|
+
return {
|
|
192
|
+
intToString: (value) => writeStringResult(state, String(value), new TextEncoder()),
|
|
193
|
+
floatToString: (value) => writeStringResult(state, String(value), new TextEncoder()),
|
|
194
|
+
boolToString: (value) => writeStringResult(state, value ? "true" : "false", new TextEncoder()),
|
|
195
|
+
floatToInt: (value) => (Math.trunc(value) | 0),
|
|
196
|
+
intToFloat: (value) => value,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// =============================================================================
|
|
200
|
+
// Array builtins — operate on [length: i32][elem0: i32][elem1: i32]...
|
|
201
|
+
// =============================================================================
|
|
202
|
+
function createArrayImports(state) {
|
|
203
|
+
return {
|
|
204
|
+
array_length: (arrPtr) => {
|
|
205
|
+
return new DataView(getMemoryBuffer(state)).getInt32(arrPtr, true);
|
|
206
|
+
},
|
|
207
|
+
array_get: (arrPtr, index) => {
|
|
208
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
209
|
+
const length = view.getInt32(arrPtr, true);
|
|
210
|
+
if (index < 0 || index >= length)
|
|
211
|
+
return 0;
|
|
212
|
+
return view.getInt32(arrPtr + 4 + index * 4, true);
|
|
213
|
+
},
|
|
214
|
+
array_set: (arrPtr, index, value) => {
|
|
215
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
216
|
+
const length = view.getInt32(arrPtr, true);
|
|
217
|
+
const elems = [];
|
|
218
|
+
for (let i = 0; i < length; i++) {
|
|
219
|
+
elems.push(i === index ? value : view.getInt32(arrPtr + 4 + i * 4, true));
|
|
220
|
+
}
|
|
221
|
+
return writeArrayResult(state, elems);
|
|
222
|
+
},
|
|
223
|
+
array_push: (arrPtr, value) => {
|
|
224
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
225
|
+
const length = view.getInt32(arrPtr, true);
|
|
226
|
+
const elems = [];
|
|
227
|
+
for (let i = 0; i < length; i++) {
|
|
228
|
+
elems.push(view.getInt32(arrPtr + 4 + i * 4, true));
|
|
229
|
+
}
|
|
230
|
+
elems.push(value);
|
|
231
|
+
return writeArrayResult(state, elems);
|
|
232
|
+
},
|
|
233
|
+
array_pop: (arrPtr) => {
|
|
234
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
235
|
+
const length = view.getInt32(arrPtr, true);
|
|
236
|
+
if (length === 0)
|
|
237
|
+
return writeArrayResult(state, []);
|
|
238
|
+
const elems = [];
|
|
239
|
+
for (let i = 0; i < length - 1; i++) {
|
|
240
|
+
elems.push(view.getInt32(arrPtr + 4 + i * 4, true));
|
|
241
|
+
}
|
|
242
|
+
return writeArrayResult(state, elems);
|
|
243
|
+
},
|
|
244
|
+
array_concat: (aPtr, bPtr) => {
|
|
245
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
246
|
+
const aLen = view.getInt32(aPtr, true);
|
|
247
|
+
const bLen = view.getInt32(bPtr, true);
|
|
248
|
+
const elems = [];
|
|
249
|
+
for (let i = 0; i < aLen; i++) {
|
|
250
|
+
elems.push(view.getInt32(aPtr + 4 + i * 4, true));
|
|
251
|
+
}
|
|
252
|
+
for (let i = 0; i < bLen; i++) {
|
|
253
|
+
elems.push(view.getInt32(bPtr + 4 + i * 4, true));
|
|
254
|
+
}
|
|
255
|
+
return writeArrayResult(state, elems);
|
|
256
|
+
},
|
|
257
|
+
array_slice: (arrPtr, start, end) => {
|
|
258
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
259
|
+
const length = view.getInt32(arrPtr, true);
|
|
260
|
+
const s = Math.max(0, Math.min(start, length));
|
|
261
|
+
const e = Math.max(s, Math.min(end, length));
|
|
262
|
+
const elems = [];
|
|
263
|
+
for (let i = s; i < e; i++) {
|
|
264
|
+
elems.push(view.getInt32(arrPtr + 4 + i * 4, true));
|
|
265
|
+
}
|
|
266
|
+
return writeArrayResult(state, elems);
|
|
267
|
+
},
|
|
268
|
+
array_isEmpty: (arrPtr) => {
|
|
269
|
+
return new DataView(getMemoryBuffer(state)).getInt32(arrPtr, true) === 0 ? 1 : 0;
|
|
270
|
+
},
|
|
271
|
+
array_contains: (arrPtr, value) => {
|
|
272
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
273
|
+
const length = view.getInt32(arrPtr, true);
|
|
274
|
+
for (let i = 0; i < length; i++) {
|
|
275
|
+
if (view.getInt32(arrPtr + 4 + i * 4, true) === value)
|
|
276
|
+
return 1;
|
|
277
|
+
}
|
|
278
|
+
return 0;
|
|
279
|
+
},
|
|
280
|
+
array_reverse: (arrPtr) => {
|
|
281
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
282
|
+
const length = view.getInt32(arrPtr, true);
|
|
283
|
+
const elems = [];
|
|
284
|
+
for (let i = length - 1; i >= 0; i--) {
|
|
285
|
+
elems.push(view.getInt32(arrPtr + 4 + i * 4, true));
|
|
286
|
+
}
|
|
287
|
+
return writeArrayResult(state, elems);
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// =============================================================================
|
|
292
|
+
// Option builtins — [tag: i32][value: i32] at 8-byte slots
|
|
293
|
+
// =============================================================================
|
|
294
|
+
function createOptionImports(state) {
|
|
295
|
+
return {
|
|
296
|
+
isSome: (ptr) => {
|
|
297
|
+
return new DataView(getMemoryBuffer(state)).getInt32(ptr, true) === 1 ? 1 : 0;
|
|
298
|
+
},
|
|
299
|
+
isNone: (ptr) => {
|
|
300
|
+
return new DataView(getMemoryBuffer(state)).getInt32(ptr, true) === 0 ? 1 : 0;
|
|
301
|
+
},
|
|
302
|
+
unwrap: (ptr) => {
|
|
303
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
304
|
+
const tag = view.getInt32(ptr, true);
|
|
305
|
+
if (tag === 1)
|
|
306
|
+
return view.getInt32(ptr + 8, true);
|
|
307
|
+
throw new Error("unwrap called on None");
|
|
308
|
+
},
|
|
309
|
+
unwrapOr: (ptr, defaultVal) => {
|
|
310
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
311
|
+
const tag = view.getInt32(ptr, true);
|
|
312
|
+
if (tag === 1)
|
|
313
|
+
return view.getInt32(ptr + 8, true);
|
|
314
|
+
return defaultVal;
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
// =============================================================================
|
|
319
|
+
// Result builtins — [tag: i32][value_or_error: i32] at 8-byte slots
|
|
320
|
+
// Ok = tag 0, Err = tag 1
|
|
321
|
+
// =============================================================================
|
|
322
|
+
function createResultImports(state) {
|
|
323
|
+
return {
|
|
324
|
+
isOk: (ptr) => {
|
|
325
|
+
return new DataView(getMemoryBuffer(state)).getInt32(ptr, true) === 0 ? 1 : 0;
|
|
326
|
+
},
|
|
327
|
+
isErr: (ptr) => {
|
|
328
|
+
return new DataView(getMemoryBuffer(state)).getInt32(ptr, true) === 1 ? 1 : 0;
|
|
329
|
+
},
|
|
330
|
+
unwrapOk: (ptr) => {
|
|
331
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
332
|
+
const tag = view.getInt32(ptr, true);
|
|
333
|
+
if (tag === 0)
|
|
334
|
+
return view.getInt32(ptr + 8, true);
|
|
335
|
+
throw new Error("unwrapOk called on Err");
|
|
336
|
+
},
|
|
337
|
+
unwrapErr: (ptr) => {
|
|
338
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
339
|
+
const tag = view.getInt32(ptr, true);
|
|
340
|
+
if (tag === 1)
|
|
341
|
+
return view.getInt32(ptr + 8, true);
|
|
342
|
+
throw new Error("unwrapErr called on Ok");
|
|
343
|
+
},
|
|
344
|
+
unwrapOkOr: (ptr, defaultVal) => {
|
|
345
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
346
|
+
const tag = view.getInt32(ptr, true);
|
|
347
|
+
if (tag === 0)
|
|
348
|
+
return view.getInt32(ptr + 8, true);
|
|
349
|
+
return defaultVal;
|
|
350
|
+
},
|
|
351
|
+
unwrapErrOr: (ptr, defaultVal) => {
|
|
352
|
+
const view = new DataView(getMemoryBuffer(state));
|
|
353
|
+
const tag = view.getInt32(ptr, true);
|
|
354
|
+
if (tag === 1)
|
|
355
|
+
return view.getInt32(ptr + 8, true);
|
|
356
|
+
return defaultVal;
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
// =============================================================================
|
|
361
|
+
// JSON builtins — jsonParse validates JSON, jsonStringify normalizes
|
|
362
|
+
// =============================================================================
|
|
363
|
+
function createJsonImports(state) {
|
|
364
|
+
const encoder = new TextEncoder();
|
|
365
|
+
const decoder = new TextDecoder();
|
|
366
|
+
return {
|
|
367
|
+
jsonParse: (ptr, len) => {
|
|
368
|
+
const str = decoder.decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
369
|
+
try {
|
|
370
|
+
JSON.parse(str);
|
|
371
|
+
// Valid JSON — return Ok(strPtr) with original string
|
|
372
|
+
const strPtr = writeStringResult(state, str, encoder);
|
|
373
|
+
return writeResultValue(state, 0, strPtr); // Ok
|
|
374
|
+
}
|
|
375
|
+
catch (e) {
|
|
376
|
+
const msg = e instanceof Error ? e.message : "Invalid JSON";
|
|
377
|
+
const errPtr = writeStringResult(state, msg, encoder);
|
|
378
|
+
return writeResultValue(state, 1, errPtr); // Err
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
jsonStringify: (ptr, len) => {
|
|
382
|
+
const str = decoder.decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
383
|
+
try {
|
|
384
|
+
const parsed = JSON.parse(str);
|
|
385
|
+
return writeStringResult(state, JSON.stringify(parsed), encoder);
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
// If input is not valid JSON, return it unchanged
|
|
389
|
+
return writeStringResult(state, str, encoder);
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
// =============================================================================
|
|
395
|
+
// Random builtins — randomInt, randomFloat, randomUuid
|
|
396
|
+
// =============================================================================
|
|
397
|
+
function createRandomImports(state) {
|
|
398
|
+
const encoder = new TextEncoder();
|
|
399
|
+
return {
|
|
400
|
+
randomInt: (min, max) => {
|
|
401
|
+
// Inclusive range [min, max] with rejection sampling to avoid modulo bias
|
|
402
|
+
const range = max - min + 1;
|
|
403
|
+
const limit = 0x100000000 - (0x100000000 % range); // largest multiple of range ≤ 2^32
|
|
404
|
+
const array = new Uint32Array(1);
|
|
405
|
+
let val;
|
|
406
|
+
do {
|
|
407
|
+
crypto.getRandomValues(array);
|
|
408
|
+
val = array[0];
|
|
409
|
+
} while (val >= limit);
|
|
410
|
+
return min + (val % range);
|
|
411
|
+
},
|
|
412
|
+
randomFloat: () => {
|
|
413
|
+
const array = new Uint32Array(1);
|
|
414
|
+
crypto.getRandomValues(array);
|
|
415
|
+
return array[0] / 0x100000000; // [0, 1) — divide by 2^32
|
|
416
|
+
},
|
|
417
|
+
randomUuid: () => {
|
|
418
|
+
const uuid = crypto.randomUUID();
|
|
419
|
+
return writeStringResult(state, uuid, encoder);
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
// =============================================================================
|
|
424
|
+
// Int64 conversion builtins — widen/narrow between Int and Int64
|
|
425
|
+
// =============================================================================
|
|
426
|
+
function createInt64Imports(state) {
|
|
427
|
+
const encoder = new TextEncoder();
|
|
428
|
+
return {
|
|
429
|
+
intToInt64: (x) => BigInt(x),
|
|
430
|
+
int64ToInt: (x) => Number(BigInt.asIntN(32, x)),
|
|
431
|
+
int64ToFloat: (x) => Number(x),
|
|
432
|
+
int64ToString: (x) => writeStringResult(state, x.toString(), encoder),
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
// =============================================================================
|
|
436
|
+
// Date/time builtins — now, formatDate, parseDate, diffMs
|
|
437
|
+
// =============================================================================
|
|
438
|
+
/**
|
|
439
|
+
* Format a Date using strftime-style tokens.
|
|
440
|
+
* Supported: %Y (year), %m (month 01-12), %d (day 01-31),
|
|
441
|
+
* %H (hour 00-23), %M (min 00-59), %S (sec 00-59), %% (literal %)
|
|
442
|
+
*/
|
|
443
|
+
function formatDateString(date, fmt) {
|
|
444
|
+
const pad2 = (n) => String(n).padStart(2, "0");
|
|
445
|
+
let result = "";
|
|
446
|
+
let i = 0;
|
|
447
|
+
while (i < fmt.length) {
|
|
448
|
+
if (fmt[i] === "%" && i + 1 < fmt.length) {
|
|
449
|
+
const token = fmt[i + 1];
|
|
450
|
+
switch (token) {
|
|
451
|
+
case "Y":
|
|
452
|
+
result += String(date.getUTCFullYear());
|
|
453
|
+
break;
|
|
454
|
+
case "m":
|
|
455
|
+
result += pad2(date.getUTCMonth() + 1);
|
|
456
|
+
break;
|
|
457
|
+
case "d":
|
|
458
|
+
result += pad2(date.getUTCDate());
|
|
459
|
+
break;
|
|
460
|
+
case "H":
|
|
461
|
+
result += pad2(date.getUTCHours());
|
|
462
|
+
break;
|
|
463
|
+
case "M":
|
|
464
|
+
result += pad2(date.getUTCMinutes());
|
|
465
|
+
break;
|
|
466
|
+
case "S":
|
|
467
|
+
result += pad2(date.getUTCSeconds());
|
|
468
|
+
break;
|
|
469
|
+
case "%":
|
|
470
|
+
result += "%";
|
|
471
|
+
break;
|
|
472
|
+
default:
|
|
473
|
+
result += "%" + token;
|
|
474
|
+
break; // unknown token → pass through
|
|
475
|
+
}
|
|
476
|
+
i += 2;
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
result += fmt[i];
|
|
480
|
+
i++;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return result;
|
|
484
|
+
}
|
|
485
|
+
function createDateTimeImports(state) {
|
|
486
|
+
const encoder = new TextEncoder();
|
|
487
|
+
const decoder = new TextDecoder();
|
|
488
|
+
return {
|
|
489
|
+
now: () => BigInt(Date.now()),
|
|
490
|
+
formatDate: (timestamp, fmtPtr, fmtLen) => {
|
|
491
|
+
const fmt = decoder.decode(new Uint8Array(getMemoryBuffer(state), fmtPtr, fmtLen));
|
|
492
|
+
const date = new Date(Number(timestamp));
|
|
493
|
+
return writeStringResult(state, formatDateString(date, fmt), encoder);
|
|
494
|
+
},
|
|
495
|
+
parseDate: (strPtr, strLen, _fmtPtr, _fmtLen) => {
|
|
496
|
+
const str = decoder.decode(new Uint8Array(getMemoryBuffer(state), strPtr, strLen));
|
|
497
|
+
const ms = Date.parse(str);
|
|
498
|
+
if (isNaN(ms)) {
|
|
499
|
+
throw new Error(`parseDate: invalid date string "${str}"`);
|
|
500
|
+
}
|
|
501
|
+
return BigInt(ms);
|
|
502
|
+
},
|
|
503
|
+
diffMs: (a, b) => a - b,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
// =============================================================================
|
|
507
|
+
// Regex builtins — regexTest, regexMatch, regexReplace
|
|
508
|
+
// =============================================================================
|
|
509
|
+
function createRegexImports(state) {
|
|
510
|
+
const encoder = new TextEncoder();
|
|
511
|
+
const decoder = new TextDecoder();
|
|
512
|
+
return {
|
|
513
|
+
regexTest: (patPtr, patLen, inputPtr, inputLen) => {
|
|
514
|
+
const buf = getMemoryBuffer(state);
|
|
515
|
+
const pattern = decoder.decode(new Uint8Array(buf, patPtr, patLen));
|
|
516
|
+
const input = decoder.decode(new Uint8Array(buf, inputPtr, inputLen));
|
|
517
|
+
try {
|
|
518
|
+
return new RegExp(pattern).test(input) ? 1 : 0;
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
return 0; // invalid regex → false
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
regexMatch: (patPtr, patLen, inputPtr, inputLen) => {
|
|
525
|
+
const buf = getMemoryBuffer(state);
|
|
526
|
+
const pattern = decoder.decode(new Uint8Array(buf, patPtr, patLen));
|
|
527
|
+
const input = decoder.decode(new Uint8Array(buf, inputPtr, inputLen));
|
|
528
|
+
try {
|
|
529
|
+
const m = input.match(new RegExp(pattern));
|
|
530
|
+
return writeStringResult(state, m ? m[0] : "", encoder);
|
|
531
|
+
}
|
|
532
|
+
catch {
|
|
533
|
+
return writeStringResult(state, "", encoder); // invalid regex → empty string
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
regexReplace: (inputPtr, inputLen, patPtr, patLen, replPtr, replLen) => {
|
|
537
|
+
const buf = getMemoryBuffer(state);
|
|
538
|
+
const input = decoder.decode(new Uint8Array(buf, inputPtr, inputLen));
|
|
539
|
+
const pattern = decoder.decode(new Uint8Array(buf, patPtr, patLen));
|
|
540
|
+
const replacement = decoder.decode(new Uint8Array(buf, replPtr, replLen));
|
|
541
|
+
try {
|
|
542
|
+
return writeStringResult(state, input.replace(new RegExp(pattern, "g"), replacement), encoder);
|
|
543
|
+
}
|
|
544
|
+
catch {
|
|
545
|
+
return writeStringResult(state, input, encoder); // invalid regex → unchanged
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
// =============================================================================
|
|
551
|
+
// Crypto hashing builtins — delegates to adapter
|
|
552
|
+
// =============================================================================
|
|
553
|
+
function createCryptoImports(state, adapter) {
|
|
554
|
+
const encoder = new TextEncoder();
|
|
555
|
+
const decoder = new TextDecoder();
|
|
556
|
+
return {
|
|
557
|
+
sha256: (ptr, len) => {
|
|
558
|
+
const str = decoder.decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
559
|
+
return writeStringResult(state, adapter.sha256(str), encoder);
|
|
560
|
+
},
|
|
561
|
+
md5: (ptr, len) => {
|
|
562
|
+
const str = decoder.decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
563
|
+
return writeStringResult(state, adapter.md5(str), encoder);
|
|
564
|
+
},
|
|
565
|
+
hmac: (algoPtr, algoLen, keyPtr, keyLen, dataPtr, dataLen) => {
|
|
566
|
+
const buf = getMemoryBuffer(state);
|
|
567
|
+
const algo = decoder.decode(new Uint8Array(buf, algoPtr, algoLen));
|
|
568
|
+
const key = decoder.decode(new Uint8Array(buf, keyPtr, keyLen));
|
|
569
|
+
const data = decoder.decode(new Uint8Array(buf, dataPtr, dataLen));
|
|
570
|
+
return writeStringResult(state, adapter.hmac(algo, key, data), encoder);
|
|
571
|
+
},
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
// =============================================================================
|
|
575
|
+
// HTTP client builtins — delegates to adapter
|
|
576
|
+
// =============================================================================
|
|
577
|
+
function createHttpImports(state, adapter) {
|
|
578
|
+
const encoder = new TextEncoder();
|
|
579
|
+
const decoder = new TextDecoder();
|
|
580
|
+
function readStr(ptr, len) {
|
|
581
|
+
return decoder.decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
582
|
+
}
|
|
583
|
+
function makeResult(fetchResult) {
|
|
584
|
+
const strPtr = writeStringResult(state, fetchResult.data, encoder);
|
|
585
|
+
return writeResultValue(state, fetchResult.ok ? 0 : 1, strPtr);
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
httpGet: (urlPtr, urlLen) => {
|
|
589
|
+
return makeResult(adapter.fetch(readStr(urlPtr, urlLen), "GET"));
|
|
590
|
+
},
|
|
591
|
+
httpPost: (urlPtr, urlLen, bodyPtr, bodyLen) => {
|
|
592
|
+
return makeResult(adapter.fetch(readStr(urlPtr, urlLen), "POST", readStr(bodyPtr, bodyLen)));
|
|
593
|
+
},
|
|
594
|
+
httpPut: (urlPtr, urlLen, bodyPtr, bodyLen) => {
|
|
595
|
+
return makeResult(adapter.fetch(readStr(urlPtr, urlLen), "PUT", readStr(bodyPtr, bodyLen)));
|
|
596
|
+
},
|
|
597
|
+
httpDelete: (urlPtr, urlLen) => {
|
|
598
|
+
return makeResult(adapter.fetch(readStr(urlPtr, urlLen), "DELETE"));
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
// =============================================================================
|
|
603
|
+
// IO builtins — delegates to adapter
|
|
604
|
+
// =============================================================================
|
|
605
|
+
function createIOImports(state, adapter) {
|
|
606
|
+
const encoder = new TextEncoder();
|
|
607
|
+
const decoder = new TextDecoder();
|
|
608
|
+
function readStr(ptr, len) {
|
|
609
|
+
return decoder.decode(new Uint8Array(getMemoryBuffer(state), ptr, len));
|
|
610
|
+
}
|
|
611
|
+
return {
|
|
612
|
+
readFile: (pathPtr, pathLen) => {
|
|
613
|
+
const result = adapter.readFile(readStr(pathPtr, pathLen));
|
|
614
|
+
if (result.ok) {
|
|
615
|
+
const strPtr = writeStringResult(state, result.data, encoder);
|
|
616
|
+
return writeResultValue(state, 0, strPtr);
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
const errPtr = writeStringResult(state, result.error, encoder);
|
|
620
|
+
return writeResultValue(state, 1, errPtr);
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
writeFile: (pathPtr, pathLen, contentPtr, contentLen) => {
|
|
624
|
+
const result = adapter.writeFile(readStr(pathPtr, pathLen), readStr(contentPtr, contentLen));
|
|
625
|
+
if (result.ok) {
|
|
626
|
+
const okPtr = writeStringResult(state, "ok", encoder);
|
|
627
|
+
return writeResultValue(state, 0, okPtr);
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
const errPtr = writeStringResult(state, result.error, encoder);
|
|
631
|
+
return writeResultValue(state, 1, errPtr);
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
env: (namePtr, nameLen) => {
|
|
635
|
+
const name = readStr(namePtr, nameLen);
|
|
636
|
+
return writeStringResult(state, adapter.env(name), encoder);
|
|
637
|
+
},
|
|
638
|
+
args: () => {
|
|
639
|
+
const argsJson = JSON.stringify(adapter.args());
|
|
640
|
+
return writeStringResult(state, argsJson, encoder);
|
|
641
|
+
},
|
|
642
|
+
exit: (code) => {
|
|
643
|
+
adapter.exit(code);
|
|
644
|
+
},
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
// =============================================================================
|
|
648
|
+
// Factory — combines all groups into one import object
|
|
649
|
+
// =============================================================================
|
|
650
|
+
/**
|
|
651
|
+
* Create the complete host import object for WASM instantiation.
|
|
652
|
+
*
|
|
653
|
+
* @param state — mutable runtime state shared across all host functions.
|
|
654
|
+
* `state.instance` must be set after `WebAssembly.instantiate()`
|
|
655
|
+
* but before calling any exported WASM function.
|
|
656
|
+
* @param adapter — optional platform-specific adapter. Defaults to NodeHostAdapter.
|
|
657
|
+
*/
|
|
658
|
+
export function createHostImports(state, adapter) {
|
|
659
|
+
const hostAdapter = adapter ?? new NodeHostAdapter(state.sandboxDir);
|
|
660
|
+
return {
|
|
661
|
+
host: {
|
|
662
|
+
...createCoreImports(state),
|
|
663
|
+
...createStringImports(state),
|
|
664
|
+
...createMathImports(),
|
|
665
|
+
...createTypeConversionImports(state),
|
|
666
|
+
...createInt64Imports(state),
|
|
667
|
+
...createArrayImports(state),
|
|
668
|
+
...createOptionImports(state),
|
|
669
|
+
...createResultImports(state),
|
|
670
|
+
...createJsonImports(state),
|
|
671
|
+
...createRandomImports(state),
|
|
672
|
+
...createDateTimeImports(state),
|
|
673
|
+
...createRegexImports(state),
|
|
674
|
+
...createCryptoImports(state, hostAdapter),
|
|
675
|
+
...createHttpImports(state, hostAdapter),
|
|
676
|
+
...createIOImports(state, hostAdapter),
|
|
677
|
+
},
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
//# sourceMappingURL=host-functions.js.map
|