Skip to main content

doge_compiler/
stdlib.rs

1//! The compiler-side view of the standard library: the modules a `so` import can
2//! name, their members, and the `doge-runtime` function each member call wires to.
3//! Mirrors the runtime `stdlib` (like [`crate::builtins`] mirrors the builtin
4//! functions) — a member here must have a matching `{module}_{member}` function
5//! there.
6
7/// One callable member of a module: its arity, the runtime function a call emits,
8/// and the call-shape hint shown in arity diagnostics. `arity` is the required
9/// positional-argument count; `optional == true` allows exactly one extra trailing
10/// argument, so the accepted range is `arity..=arity + 1` (an omitted trailing
11/// argument reaches the runtime function as `Value::None`).
12pub struct ModuleFn {
13    pub name: &'static str,
14    pub arity: usize,
15    pub optional: bool,
16    pub runtime_fn: &'static str,
17    pub hint: &'static str,
18}
19
20impl ModuleFn {
21    /// The largest argument count this member accepts.
22    pub fn max_arity(&self) -> usize {
23        self.arity + self.optional as usize
24    }
25}
26
27/// One importable module: its name, its function members, and its constant
28/// members (each a name paired with the Rust expression codegen emits inline).
29pub struct Module {
30    pub name: &'static str,
31    pub funcs: &'static [ModuleFn],
32    pub consts: &'static [(&'static str, &'static str)],
33}
34
35impl Module {
36    /// The function member `name`, if this module has one.
37    pub fn func(&self, name: &str) -> Option<&'static ModuleFn> {
38        self.funcs.iter().find(|f| f.name == name)
39    }
40
41    /// The Rust expression for the constant member `name`, if this module has one.
42    pub fn const_expr(&self, name: &str) -> Option<&'static str> {
43        self.consts
44            .iter()
45            .find(|(n, _)| *n == name)
46            .map(|(_, expr)| *expr)
47    }
48
49    /// Every member name, comma-joined, for the "unknown member" hint.
50    pub fn members(&self) -> String {
51        let mut names: Vec<&str> = self.funcs.iter().map(|f| f.name).collect();
52        names.extend(self.consts.iter().map(|(n, _)| *n));
53        names.join(", ")
54    }
55
56    /// The first member name, for hints that show one example call/value.
57    pub fn first_member(&self) -> &'static str {
58        self.funcs
59            .first()
60            .map(|f| f.name)
61            .or_else(|| self.consts.first().map(|(n, _)| *n))
62            .unwrap_or("")
63    }
64}
65
66/// The runtime function `pack.zoom` maps to. Both engines special-case it: the
67/// compiler hands it the pup trampoline plus a globals snapshot, and the
68/// interpreter routes it to its own thread-spawning path instead of the generic
69/// native dispatch. Kept in step with the `pack` module's `zoom` entry below.
70pub const PACK_ZOOM_RUNTIME_FN: &str = "pack_zoom";
71
72/// The module named `name`, if it exists.
73pub fn module(name: &str) -> Option<&'static Module> {
74    MODULES.iter().find(|m| m.name == name)
75}
76
77/// The comma-joined list of module names, for the "no such module" hint.
78pub(crate) fn module_names() -> String {
79    MODULES
80        .iter()
81        .map(|m| m.name)
82        .collect::<Vec<_>>()
83        .join(", ")
84}
85
86pub const MODULES: &[Module] = &[
87    Module {
88        name: "nerd",
89        funcs: &[
90            ModuleFn {
91                name: "abs",
92                arity: 1,
93                optional: false,
94                runtime_fn: "nerd_abs",
95                hint: "nerd.abs(x)",
96            },
97            ModuleFn {
98                name: "sqrt",
99                arity: 1,
100                optional: false,
101                runtime_fn: "nerd_sqrt",
102                hint: "nerd.sqrt(x)",
103            },
104            ModuleFn {
105                name: "floor",
106                arity: 1,
107                optional: false,
108                runtime_fn: "nerd_floor",
109                hint: "nerd.floor(x)",
110            },
111            ModuleFn {
112                name: "ceil",
113                arity: 1,
114                optional: false,
115                runtime_fn: "nerd_ceil",
116                hint: "nerd.ceil(x)",
117            },
118            ModuleFn {
119                name: "round",
120                arity: 1,
121                optional: false,
122                runtime_fn: "nerd_round",
123                hint: "nerd.round(x)",
124            },
125            ModuleFn {
126                name: "min",
127                arity: 2,
128                optional: false,
129                runtime_fn: "nerd_min",
130                hint: "nerd.min(a, b)",
131            },
132            ModuleFn {
133                name: "max",
134                arity: 2,
135                optional: false,
136                runtime_fn: "nerd_max",
137                hint: "nerd.max(a, b)",
138            },
139            ModuleFn {
140                name: "pow",
141                arity: 2,
142                optional: false,
143                runtime_fn: "nerd_pow",
144                hint: "nerd.pow(base, exponent)",
145            },
146        ],
147        consts: &[
148            ("pi", "Value::Float(std::f64::consts::PI)"),
149            ("e", "Value::Float(std::f64::consts::E)"),
150        ],
151    },
152    Module {
153        name: "strings",
154        funcs: &[
155            ModuleFn {
156                name: "beeg",
157                arity: 1,
158                optional: false,
159                runtime_fn: "strings_beeg",
160                hint: "strings.beeg(s)",
161            },
162            ModuleFn {
163                name: "smoll",
164                arity: 1,
165                optional: false,
166                runtime_fn: "strings_smoll",
167                hint: "strings.smoll(s)",
168            },
169            ModuleFn {
170                name: "trim",
171                arity: 1,
172                optional: false,
173                runtime_fn: "strings_trim",
174                hint: "strings.trim(s)",
175            },
176            ModuleFn {
177                name: "split",
178                arity: 2,
179                optional: false,
180                runtime_fn: "strings_split",
181                hint: "strings.split(s, sep)",
182            },
183            ModuleFn {
184                name: "join",
185                arity: 2,
186                optional: false,
187                runtime_fn: "strings_join",
188                hint: "strings.join(parts, sep)",
189            },
190            ModuleFn {
191                name: "contains",
192                arity: 2,
193                optional: false,
194                runtime_fn: "strings_contains",
195                hint: "strings.contains(s, needle)",
196            },
197            ModuleFn {
198                name: "index",
199                arity: 2,
200                optional: false,
201                runtime_fn: "strings_index",
202                hint: "strings.index(s, sub)",
203            },
204            ModuleFn {
205                name: "replace",
206                arity: 3,
207                optional: false,
208                runtime_fn: "strings_replace",
209                hint: "strings.replace(s, from, to)",
210            },
211        ],
212        consts: &[],
213    },
214    Module {
215        name: "hunt",
216        funcs: &[
217            ModuleFn {
218                name: "test",
219                arity: 2,
220                optional: false,
221                runtime_fn: "hunt_test",
222                hint: "hunt.test(pat, text)",
223            },
224            ModuleFn {
225                name: "find",
226                arity: 2,
227                optional: false,
228                runtime_fn: "hunt_find",
229                hint: "hunt.find(pat, text)",
230            },
231            ModuleFn {
232                name: "find_all",
233                arity: 2,
234                optional: false,
235                runtime_fn: "hunt_find_all",
236                hint: "hunt.find_all(pat, text)",
237            },
238            ModuleFn {
239                name: "groups",
240                arity: 2,
241                optional: false,
242                runtime_fn: "hunt_groups",
243                hint: "hunt.groups(pat, text)",
244            },
245            ModuleFn {
246                name: "replace",
247                arity: 3,
248                optional: false,
249                runtime_fn: "hunt_replace",
250                hint: "hunt.replace(pat, text, repl)",
251            },
252        ],
253        consts: &[],
254    },
255    Module {
256        name: "fetch",
257        funcs: &[
258            ModuleFn {
259                name: "read",
260                arity: 1,
261                optional: false,
262                runtime_fn: "fetch_read",
263                hint: "fetch.read(path)",
264            },
265            ModuleFn {
266                name: "write",
267                arity: 2,
268                optional: false,
269                runtime_fn: "fetch_write",
270                hint: "fetch.write(path, text)",
271            },
272            ModuleFn {
273                name: "append",
274                arity: 2,
275                optional: false,
276                runtime_fn: "fetch_append",
277                hint: "fetch.append(path, text)",
278            },
279            ModuleFn {
280                name: "read_bytes",
281                arity: 1,
282                optional: false,
283                runtime_fn: "fetch_read_bytes",
284                hint: "fetch.read_bytes(path)",
285            },
286            ModuleFn {
287                name: "write_bytes",
288                arity: 2,
289                optional: false,
290                runtime_fn: "fetch_write_bytes",
291                hint: "fetch.write_bytes(path, bytes)",
292            },
293            ModuleFn {
294                name: "exists",
295                arity: 1,
296                optional: false,
297                runtime_fn: "fetch_exists",
298                hint: "fetch.exists(path)",
299            },
300            ModuleFn {
301                name: "delete",
302                arity: 1,
303                optional: false,
304                runtime_fn: "fetch_delete",
305                hint: "fetch.delete(path)",
306            },
307            ModuleFn {
308                name: "list",
309                arity: 1,
310                optional: false,
311                runtime_fn: "fetch_list",
312                hint: "fetch.list(path)",
313            },
314            ModuleFn {
315                name: "make_dir",
316                arity: 1,
317                optional: false,
318                runtime_fn: "fetch_make_dir",
319                hint: "fetch.make_dir(path)",
320            },
321            ModuleFn {
322                name: "remove_dir",
323                arity: 1,
324                optional: false,
325                runtime_fn: "fetch_remove_dir",
326                hint: "fetch.remove_dir(path)",
327            },
328            ModuleFn {
329                name: "rename",
330                arity: 2,
331                optional: false,
332                runtime_fn: "fetch_rename",
333                hint: "fetch.rename(from, to)",
334            },
335            ModuleFn {
336                name: "copy",
337                arity: 2,
338                optional: false,
339                runtime_fn: "fetch_copy",
340                hint: "fetch.copy(from, to)",
341            },
342            ModuleFn {
343                name: "stat",
344                arity: 1,
345                optional: false,
346                runtime_fn: "fetch_stat",
347                hint: "fetch.stat(path)",
348            },
349            ModuleFn {
350                name: "join",
351                arity: 2,
352                optional: false,
353                runtime_fn: "fetch_join",
354                hint: "fetch.join(a, b)",
355            },
356            ModuleFn {
357                name: "basename",
358                arity: 1,
359                optional: false,
360                runtime_fn: "fetch_basename",
361                hint: "fetch.basename(path)",
362            },
363            ModuleFn {
364                name: "ext",
365                arity: 1,
366                optional: false,
367                runtime_fn: "fetch_ext",
368                hint: "fetch.ext(path)",
369            },
370        ],
371        consts: &[],
372    },
373    Module {
374        name: "env",
375        funcs: &[
376            ModuleFn {
377                name: "args",
378                arity: 0,
379                optional: false,
380                runtime_fn: "env_args",
381                hint: "env.args()",
382            },
383            ModuleFn {
384                name: "get",
385                arity: 1,
386                optional: false,
387                runtime_fn: "env_get",
388                hint: "env.get(name)",
389            },
390        ],
391        consts: &[],
392    },
393    Module {
394        name: "howl",
395        funcs: &[
396            ModuleFn {
397                name: "listen",
398                arity: 2,
399                optional: false,
400                runtime_fn: "howl_listen",
401                hint: "howl.listen(host, port)",
402            },
403            ModuleFn {
404                name: "connect",
405                arity: 2,
406                optional: false,
407                runtime_fn: "howl_connect",
408                hint: "howl.connect(host, port)",
409            },
410            ModuleFn {
411                name: "accept",
412                arity: 1,
413                optional: false,
414                runtime_fn: "howl_accept",
415                hint: "howl.accept(listener)",
416            },
417            ModuleFn {
418                name: "port",
419                arity: 1,
420                optional: false,
421                runtime_fn: "howl_port",
422                hint: "howl.port(sock)",
423            },
424            ModuleFn {
425                name: "send",
426                arity: 2,
427                optional: false,
428                runtime_fn: "howl_send",
429                hint: "howl.send(conn, text)",
430            },
431            ModuleFn {
432                name: "send_bytes",
433                arity: 2,
434                optional: false,
435                runtime_fn: "howl_send_bytes",
436                hint: "howl.send_bytes(conn, bytes)",
437            },
438            ModuleFn {
439                name: "recv",
440                arity: 2,
441                optional: false,
442                runtime_fn: "howl_recv",
443                hint: "howl.recv(conn, max_bytes)",
444            },
445            ModuleFn {
446                name: "recv_bytes",
447                arity: 2,
448                optional: false,
449                runtime_fn: "howl_recv_bytes",
450                hint: "howl.recv_bytes(conn, max_bytes)",
451            },
452            ModuleFn {
453                name: "recv_line",
454                arity: 1,
455                optional: false,
456                runtime_fn: "howl_recv_line",
457                hint: "howl.recv_line(conn)",
458            },
459            ModuleFn {
460                name: "close",
461                arity: 1,
462                optional: false,
463                runtime_fn: "howl_close",
464                hint: "howl.close(sock)",
465            },
466            ModuleFn {
467                name: "get",
468                arity: 1,
469                optional: false,
470                runtime_fn: "howl_get",
471                hint: "howl.get(url)",
472            },
473            ModuleFn {
474                name: "post",
475                arity: 2,
476                optional: false,
477                runtime_fn: "howl_post",
478                hint: "howl.post(url, body)",
479            },
480            ModuleFn {
481                name: "request",
482                arity: 2,
483                optional: true,
484                runtime_fn: "howl_request",
485                hint: "howl.request(method, url) or howl.request(method, url, opts)",
486            },
487        ],
488        consts: &[],
489    },
490    Module {
491        name: "json",
492        funcs: &[
493            ModuleFn {
494                name: "parse",
495                arity: 1,
496                optional: false,
497                runtime_fn: "json_parse",
498                hint: "json.parse(text)",
499            },
500            ModuleFn {
501                name: "emit",
502                arity: 1,
503                optional: false,
504                runtime_fn: "json_emit",
505                hint: "json.emit(value)",
506            },
507        ],
508        consts: &[],
509    },
510    Module {
511        name: "dson",
512        funcs: &[
513            ModuleFn {
514                name: "parse",
515                arity: 1,
516                optional: false,
517                runtime_fn: "dson_parse",
518                hint: "dson.parse(text)",
519            },
520            ModuleFn {
521                name: "emit",
522                arity: 1,
523                optional: false,
524                runtime_fn: "dson_emit",
525                hint: "dson.emit(value)",
526            },
527        ],
528        consts: &[],
529    },
530    Module {
531        name: "nap",
532        funcs: &[
533            ModuleFn {
534                name: "now",
535                arity: 0,
536                optional: false,
537                runtime_fn: "nap_now",
538                hint: "nap.now()",
539            },
540            ModuleFn {
541                name: "mono",
542                arity: 0,
543                optional: false,
544                runtime_fn: "nap_mono",
545                hint: "nap.mono()",
546            },
547            ModuleFn {
548                name: "rest",
549                arity: 1,
550                optional: false,
551                runtime_fn: "nap_rest",
552                hint: "nap.rest(seconds)",
553            },
554            ModuleFn {
555                name: "stamp",
556                arity: 1,
557                optional: false,
558                runtime_fn: "nap_stamp",
559                hint: "nap.stamp(secs)",
560            },
561            ModuleFn {
562                name: "parse",
563                arity: 1,
564                optional: false,
565                runtime_fn: "nap_parse",
566                hint: "nap.parse(text)",
567            },
568        ],
569        consts: &[],
570    },
571    Module {
572        name: "pack",
573        funcs: &[
574            // `zoom` is special in codegen: it also receives the generated pup
575            // trampoline and a snapshot of the globals (see `PACK_ZOOM_RUNTIME_FN`),
576            // so its two members here are the two the user actually writes.
577            ModuleFn {
578                name: "zoom",
579                arity: 2,
580                optional: false,
581                runtime_fn: PACK_ZOOM_RUNTIME_FN,
582                hint: "pack.zoom(f, [args])",
583            },
584            ModuleFn {
585                name: "fetch",
586                arity: 1,
587                optional: false,
588                runtime_fn: "pack_fetch",
589                hint: "pack.fetch(pup)",
590            },
591            ModuleFn {
592                name: "bowl",
593                arity: 0,
594                optional: false,
595                runtime_fn: "pack_bowl",
596                hint: "pack.bowl()",
597            },
598            ModuleFn {
599                name: "drop",
600                arity: 2,
601                optional: false,
602                runtime_fn: "pack_drop",
603                hint: "pack.drop(bowl, value)",
604            },
605            ModuleFn {
606                name: "sniff",
607                arity: 1,
608                optional: false,
609                runtime_fn: "pack_sniff",
610                hint: "pack.sniff(bowl)",
611            },
612        ],
613        consts: &[],
614    },
615    Module {
616        name: "chase",
617        funcs: &[ModuleFn {
618            name: "run",
619            arity: 3,
620            optional: false,
621            runtime_fn: "chase_run",
622            hint: "chase.run(cmd, args, stdin)",
623        }],
624        consts: &[],
625    },
626    Module {
627        name: "roll",
628        funcs: &[
629            ModuleFn {
630                name: "seed",
631                arity: 1,
632                optional: false,
633                runtime_fn: "roll_seed",
634                hint: "roll.seed(n)",
635            },
636            ModuleFn {
637                name: "int",
638                arity: 2,
639                optional: false,
640                runtime_fn: "roll_int",
641                hint: "roll.int(low, high)",
642            },
643            ModuleFn {
644                name: "float",
645                arity: 0,
646                optional: false,
647                runtime_fn: "roll_float",
648                hint: "roll.float()",
649            },
650            ModuleFn {
651                name: "choice",
652                arity: 1,
653                optional: false,
654                runtime_fn: "roll_choice",
655                hint: "roll.choice(list)",
656            },
657            ModuleFn {
658                name: "shuffle",
659                arity: 1,
660                optional: false,
661                runtime_fn: "roll_shuffle",
662                hint: "roll.shuffle(list)",
663            },
664            ModuleFn {
665                name: "sample",
666                arity: 2,
667                optional: false,
668                runtime_fn: "roll_sample",
669                hint: "roll.sample(list, k)",
670            },
671        ],
672        consts: &[],
673    },
674    Module {
675        name: "crypto",
676        funcs: &[
677            ModuleFn {
678                name: "sha256",
679                arity: 1,
680                optional: false,
681                runtime_fn: "crypto_sha256",
682                hint: "crypto.sha256(data)",
683            },
684            ModuleFn {
685                name: "hmac_sha256",
686                arity: 2,
687                optional: false,
688                runtime_fn: "crypto_hmac_sha256",
689                hint: "crypto.hmac_sha256(key, data)",
690            },
691            ModuleFn {
692                name: "token",
693                arity: 1,
694                optional: false,
695                runtime_fn: "crypto_token",
696                hint: "crypto.token(n)",
697            },
698            ModuleFn {
699                name: "same",
700                arity: 2,
701                optional: false,
702                runtime_fn: "crypto_same",
703                hint: "crypto.same(a, b)",
704            },
705        ],
706        consts: &[],
707    },
708];