Skip to content

Commit ea62051

Browse files
committed
i wanna cry
1 parent 3442652 commit ea62051

File tree

5 files changed

+453
-17
lines changed

5 files changed

+453
-17
lines changed

src/gleam.mjs

+361
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
// Values marked with @internal are not part of the public API and may change
2+
// without notice.
3+
4+
export class CustomType {
5+
withFields(fields) {
6+
let properties = Object.keys(this).map((label) =>
7+
label in fields ? fields[label] : this[label]
8+
);
9+
return new this.constructor(...properties);
10+
}
11+
}
12+
13+
export class List {
14+
static fromArray(array, tail) {
15+
let t = tail || new Empty();
16+
for (let i = array.length - 1; i >= 0; --i) {
17+
t = new NonEmpty(array[i], t);
18+
}
19+
return t;
20+
}
21+
22+
[Symbol.iterator]() {
23+
return new ListIterator(this);
24+
}
25+
26+
toArray() {
27+
return [...this];
28+
}
29+
30+
// @internal
31+
atLeastLength(desired) {
32+
for (let _ of this) {
33+
if (desired <= 0) return true;
34+
desired--;
35+
}
36+
return desired <= 0;
37+
}
38+
39+
// @internal
40+
hasLength(desired) {
41+
for (let _ of this) {
42+
if (desired <= 0) return false;
43+
desired--;
44+
}
45+
return desired === 0;
46+
}
47+
48+
countLength() {
49+
let length = 0;
50+
for (let _ of this) length++;
51+
return length;
52+
}
53+
}
54+
55+
// @internal
56+
export function prepend(element, tail) {
57+
return new NonEmpty(element, tail);
58+
}
59+
60+
export function toList(elements, tail) {
61+
return List.fromArray(elements, tail);
62+
}
63+
64+
// @internal
65+
class ListIterator {
66+
#current;
67+
68+
constructor(current) {
69+
this.#current = current;
70+
}
71+
72+
next() {
73+
if (this.#current instanceof Empty) {
74+
return { done: true };
75+
} else {
76+
let { head, tail } = this.#current;
77+
this.#current = tail;
78+
return { value: head, done: false };
79+
}
80+
}
81+
}
82+
83+
export class Empty extends List {}
84+
85+
export class NonEmpty extends List {
86+
constructor(head, tail) {
87+
super();
88+
this.head = head;
89+
this.tail = tail;
90+
}
91+
}
92+
93+
export class BitArray {
94+
constructor(buffer) {
95+
if (!(buffer instanceof Uint8Array)) {
96+
throw "BitArray can only be constructed from a Uint8Array";
97+
}
98+
this.buffer = buffer;
99+
}
100+
101+
// @internal
102+
get length() {
103+
return this.buffer.length;
104+
}
105+
106+
// @internal
107+
byteAt(index) {
108+
return this.buffer[index];
109+
}
110+
111+
// @internal
112+
floatAt(index) {
113+
return byteArrayToFloat(this.buffer.slice(index, index + 8));
114+
}
115+
116+
// @internal
117+
intFromSlice(start, end) {
118+
return byteArrayToInt(this.buffer.slice(start, end));
119+
}
120+
121+
// @internal
122+
binaryFromSlice(start, end) {
123+
return new BitArray(this.buffer.slice(start, end));
124+
}
125+
126+
// @internal
127+
sliceAfter(index) {
128+
return new BitArray(this.buffer.slice(index));
129+
}
130+
}
131+
132+
export class UtfCodepoint {
133+
constructor(value) {
134+
this.value = value;
135+
}
136+
}
137+
138+
// @internal
139+
export function toBitArray(segments) {
140+
let size = (segment) =>
141+
segment instanceof Uint8Array ? segment.byteLength : 1;
142+
let bytes = segments.reduce((acc, segment) => acc + size(segment), 0);
143+
let view = new DataView(new ArrayBuffer(bytes));
144+
let cursor = 0;
145+
for (let segment of segments) {
146+
if (segment instanceof Uint8Array) {
147+
new Uint8Array(view.buffer).set(segment, cursor);
148+
cursor += segment.byteLength;
149+
} else {
150+
view.setInt8(cursor, segment);
151+
cursor++;
152+
}
153+
}
154+
return new BitArray(new Uint8Array(view.buffer));
155+
}
156+
157+
// @internal
158+
// Derived from this answer https://stackoverflow.com/questions/8482309/converting-javascript-integer-to-byte-array-and-back
159+
export function sizedInt(int, size) {
160+
let value = int;
161+
if (size < 0) {
162+
return new Uint8Array();
163+
}
164+
if (size % 8 != 0) {
165+
const msg =
166+
`Bit arrays must be byte aligned on JavaScript, got size of ${size} bits`;
167+
throw new globalThis.Error(msg);
168+
}
169+
const byteArray = new Uint8Array(size / 8);
170+
171+
for (let index = 0; index < byteArray.length; index++) {
172+
const byte = value & 0xff;
173+
byteArray[index] = byte;
174+
value = (value - byte) / 256;
175+
}
176+
return byteArray.reverse();
177+
}
178+
179+
// @internal
180+
export function byteArrayToInt(byteArray) {
181+
byteArray = byteArray.reverse();
182+
let value = 0;
183+
for (let i = byteArray.length - 1; i >= 0; i--) {
184+
value = value * 256 + byteArray[i];
185+
}
186+
return value;
187+
}
188+
189+
// @internal
190+
export function byteArrayToFloat(byteArray) {
191+
return new Float64Array(byteArray.reverse().buffer)[0];
192+
}
193+
194+
// @internal
195+
export function stringBits(string) {
196+
return new TextEncoder().encode(string);
197+
}
198+
199+
// @internal
200+
export function codepointBits(codepoint) {
201+
return stringBits(String.fromCodePoint(codepoint.value));
202+
}
203+
204+
// @internal
205+
export function float64Bits(float) {
206+
return new Uint8Array(Float64Array.from([float]).buffer).reverse();
207+
}
208+
209+
export class Result extends CustomType {
210+
// @internal
211+
static isResult(data) {
212+
return data instanceof Result;
213+
}
214+
}
215+
216+
export class Ok extends Result {
217+
constructor(value) {
218+
super();
219+
this[0] = value;
220+
}
221+
222+
// @internal
223+
isOk() {
224+
return true;
225+
}
226+
}
227+
228+
export class Error extends Result {
229+
constructor(detail) {
230+
super();
231+
this[0] = detail;
232+
}
233+
234+
// @internal
235+
isOk() {
236+
return false;
237+
}
238+
}
239+
240+
export function isEqual(x, y) {
241+
let values = [x, y];
242+
243+
while (values.length) {
244+
let a = values.pop();
245+
let b = values.pop();
246+
if (a === b) continue;
247+
248+
if (!isObject(a) || !isObject(b)) return false;
249+
let unequal = !structurallyCompatibleObjects(a, b) ||
250+
unequalDates(a, b) ||
251+
unequalBuffers(a, b) ||
252+
unequalArrays(a, b) ||
253+
unequalMaps(a, b) ||
254+
unequalSets(a, b) ||
255+
unequalRegExps(a, b);
256+
if (unequal) return false;
257+
258+
const proto = Object.getPrototypeOf(a);
259+
if (proto !== null && typeof proto.equals === "function") {
260+
try {
261+
if (a.equals(b)) continue;
262+
else return false;
263+
} catch {}
264+
}
265+
266+
let [keys, get] = getters(a);
267+
for (let k of keys(a)) {
268+
values.push(get(a, k), get(b, k));
269+
}
270+
}
271+
272+
return true;
273+
}
274+
275+
function getters(object) {
276+
if (object instanceof Map) {
277+
return [(x) => x.keys(), (x, y) => x.get(y)];
278+
} else {
279+
let extra = object instanceof globalThis.Error ? ["message"] : [];
280+
return [(x) => [...extra, ...Object.keys(x)], (x, y) => x[y]];
281+
}
282+
}
283+
284+
function unequalDates(a, b) {
285+
return a instanceof Date && (a > b || a < b);
286+
}
287+
288+
function unequalBuffers(a, b) {
289+
return (
290+
a.buffer instanceof ArrayBuffer &&
291+
a.BYTES_PER_ELEMENT &&
292+
!(a.byteLength === b.byteLength && a.every((n, i) => n === b[i]))
293+
);
294+
}
295+
296+
function unequalArrays(a, b) {
297+
return Array.isArray(a) && a.length !== b.length;
298+
}
299+
300+
function unequalMaps(a, b) {
301+
return a instanceof Map && a.size !== b.size;
302+
}
303+
304+
function unequalSets(a, b) {
305+
return (
306+
a instanceof Set && (a.size != b.size || [...a].some((e) => !b.has(e)))
307+
);
308+
}
309+
310+
function unequalRegExps(a, b) {
311+
return a instanceof RegExp && (a.source !== b.source || a.flags !== b.flags);
312+
}
313+
314+
function isObject(a) {
315+
return typeof a === "object" && a !== null;
316+
}
317+
318+
function structurallyCompatibleObjects(a, b) {
319+
if (typeof a !== "object" && typeof b !== "object" && (!a || !b)) {
320+
return false;
321+
}
322+
323+
let nonstructural = [Promise, WeakSet, WeakMap, Function];
324+
if (nonstructural.some((c) => a instanceof c)) return false;
325+
326+
return a.constructor === b.constructor;
327+
}
328+
329+
// @internal
330+
export function remainderInt(a, b) {
331+
if (b === 0) {
332+
return 0;
333+
} else {
334+
return a % b;
335+
}
336+
}
337+
338+
// @internal
339+
export function divideInt(a, b) {
340+
return Math.trunc(divideFloat(a, b));
341+
}
342+
343+
// @internal
344+
export function divideFloat(a, b) {
345+
if (b === 0) {
346+
return 0;
347+
} else {
348+
return a / b;
349+
}
350+
}
351+
352+
// @internal
353+
export function makeError(variant, module, line, fn, message, extra) {
354+
let error = new globalThis.Error(message);
355+
error.gleam_error = variant;
356+
error.module = module;
357+
error.line = line;
358+
error.fn = fn;
359+
for (let k in extra) error[k] = extra[k];
360+
return error;
361+
}

src/gleam/bit_array.gleam

+5-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ pub fn slice(
4646
) -> Result(BitArray, Nil)
4747

4848
@external(erlang, "gleam_stdlib", "bit_array_split")
49-
pub fn split(x: BitArray, on subpattern: BitArray) -> List(BitArray)
49+
@external(javascript, "../gleam_stdlib.mjs", "bit_array_split")
50+
pub fn split(
51+
bits: BitArray,
52+
on subpattern: BitArray,
53+
) -> Result(List(BitArray), Nil)
5054

5155
/// Tests to see whether a bit array is valid UTF-8.
5256
///

src/gleam_stdlib.erl

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
decode_tuple5/1, decode_tuple6/1, tuple_get/2, classify_dynamic/1, print/1,
1414
println/1, print_error/1, println_error/1, inspect/1, float_to_string/1,
1515
int_from_base_string/2, utf_codepoint_list_to_string/1, contains_string/2,
16-
crop_string/2, base16_decode/1, string_replace/3
16+
crop_string/2, base16_decode/1, string_replace/3, bit_array_split/2
1717
]).
1818

1919
%% Taken from OTP's uri_string module
@@ -207,7 +207,7 @@ bit_array_slice(Bin, Pos, Len) ->
207207
end.
208208

209209
bit_array_split(Bin, Sub) ->
210-
try {ok, binary:split(Bin, Pos, Len)}
210+
try {ok, binary:split(Bin, Sub, [global])}
211211
catch error:badarg -> {error, nil}
212212
end.
213213

0 commit comments

Comments
 (0)