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: "peer",
426                arity: 1,
427                optional: false,
428                runtime_fn: "howl_peer",
429                hint: "howl.peer(sock)",
430            },
431            ModuleFn {
432                name: "send",
433                arity: 2,
434                optional: false,
435                runtime_fn: "howl_send",
436                hint: "howl.send(conn, text)",
437            },
438            ModuleFn {
439                name: "send_bytes",
440                arity: 2,
441                optional: false,
442                runtime_fn: "howl_send_bytes",
443                hint: "howl.send_bytes(conn, bytes)",
444            },
445            ModuleFn {
446                name: "recv",
447                arity: 2,
448                optional: false,
449                runtime_fn: "howl_recv",
450                hint: "howl.recv(conn, max_bytes)",
451            },
452            ModuleFn {
453                name: "recv_bytes",
454                arity: 2,
455                optional: false,
456                runtime_fn: "howl_recv_bytes",
457                hint: "howl.recv_bytes(conn, max_bytes)",
458            },
459            ModuleFn {
460                name: "recv_line",
461                arity: 1,
462                optional: false,
463                runtime_fn: "howl_recv_line",
464                hint: "howl.recv_line(conn)",
465            },
466            ModuleFn {
467                name: "close",
468                arity: 1,
469                optional: false,
470                runtime_fn: "howl_close",
471                hint: "howl.close(sock)",
472            },
473            ModuleFn {
474                name: "get",
475                arity: 1,
476                optional: false,
477                runtime_fn: "howl_get",
478                hint: "howl.get(url)",
479            },
480            ModuleFn {
481                name: "post",
482                arity: 2,
483                optional: false,
484                runtime_fn: "howl_post",
485                hint: "howl.post(url, body)",
486            },
487            ModuleFn {
488                name: "request",
489                arity: 2,
490                optional: true,
491                runtime_fn: "howl_request",
492                hint: "howl.request(method, url) or howl.request(method, url, opts)",
493            },
494        ],
495        consts: &[],
496    },
497    Module {
498        name: "json",
499        funcs: &[
500            ModuleFn {
501                name: "parse",
502                arity: 1,
503                optional: false,
504                runtime_fn: "json_parse",
505                hint: "json.parse(text)",
506            },
507            ModuleFn {
508                name: "emit",
509                arity: 1,
510                optional: false,
511                runtime_fn: "json_emit",
512                hint: "json.emit(value)",
513            },
514        ],
515        consts: &[],
516    },
517    Module {
518        name: "dson",
519        funcs: &[
520            ModuleFn {
521                name: "parse",
522                arity: 1,
523                optional: false,
524                runtime_fn: "dson_parse",
525                hint: "dson.parse(text)",
526            },
527            ModuleFn {
528                name: "emit",
529                arity: 1,
530                optional: false,
531                runtime_fn: "dson_emit",
532                hint: "dson.emit(value)",
533            },
534        ],
535        consts: &[],
536    },
537    Module {
538        name: "nap",
539        funcs: &[
540            ModuleFn {
541                name: "now",
542                arity: 0,
543                optional: false,
544                runtime_fn: "nap_now",
545                hint: "nap.now()",
546            },
547            ModuleFn {
548                name: "mono",
549                arity: 0,
550                optional: false,
551                runtime_fn: "nap_mono",
552                hint: "nap.mono()",
553            },
554            ModuleFn {
555                name: "rest",
556                arity: 1,
557                optional: false,
558                runtime_fn: "nap_rest",
559                hint: "nap.rest(seconds)",
560            },
561            ModuleFn {
562                name: "stamp",
563                arity: 1,
564                optional: false,
565                runtime_fn: "nap_stamp",
566                hint: "nap.stamp(secs)",
567            },
568            ModuleFn {
569                name: "parse",
570                arity: 1,
571                optional: false,
572                runtime_fn: "nap_parse",
573                hint: "nap.parse(text)",
574            },
575        ],
576        consts: &[],
577    },
578    Module {
579        name: "pack",
580        funcs: &[
581            // `zoom` is special in codegen: it also receives the generated pup
582            // trampoline and a snapshot of the globals (see `PACK_ZOOM_RUNTIME_FN`),
583            // so its two members here are the two the user actually writes.
584            ModuleFn {
585                name: "zoom",
586                arity: 2,
587                optional: false,
588                runtime_fn: PACK_ZOOM_RUNTIME_FN,
589                hint: "pack.zoom(f, [args])",
590            },
591            ModuleFn {
592                name: "fetch",
593                arity: 1,
594                optional: false,
595                runtime_fn: "pack_fetch",
596                hint: "pack.fetch(pup)",
597            },
598            ModuleFn {
599                name: "bowl",
600                arity: 0,
601                optional: false,
602                runtime_fn: "pack_bowl",
603                hint: "pack.bowl()",
604            },
605            ModuleFn {
606                name: "drop",
607                arity: 2,
608                optional: false,
609                runtime_fn: "pack_drop",
610                hint: "pack.drop(bowl, value)",
611            },
612            ModuleFn {
613                name: "sniff",
614                arity: 1,
615                optional: false,
616                runtime_fn: "pack_sniff",
617                hint: "pack.sniff(bowl)",
618            },
619        ],
620        consts: &[],
621    },
622    Module {
623        name: "chase",
624        funcs: &[ModuleFn {
625            name: "run",
626            arity: 3,
627            optional: false,
628            runtime_fn: "chase_run",
629            hint: "chase.run(cmd, args, stdin)",
630        }],
631        consts: &[],
632    },
633    Module {
634        name: "roll",
635        funcs: &[
636            ModuleFn {
637                name: "seed",
638                arity: 1,
639                optional: false,
640                runtime_fn: "roll_seed",
641                hint: "roll.seed(n)",
642            },
643            ModuleFn {
644                name: "int",
645                arity: 2,
646                optional: false,
647                runtime_fn: "roll_int",
648                hint: "roll.int(low, high)",
649            },
650            ModuleFn {
651                name: "float",
652                arity: 0,
653                optional: false,
654                runtime_fn: "roll_float",
655                hint: "roll.float()",
656            },
657            ModuleFn {
658                name: "choice",
659                arity: 1,
660                optional: false,
661                runtime_fn: "roll_choice",
662                hint: "roll.choice(list)",
663            },
664            ModuleFn {
665                name: "shuffle",
666                arity: 1,
667                optional: false,
668                runtime_fn: "roll_shuffle",
669                hint: "roll.shuffle(list)",
670            },
671            ModuleFn {
672                name: "sample",
673                arity: 2,
674                optional: false,
675                runtime_fn: "roll_sample",
676                hint: "roll.sample(list, k)",
677            },
678        ],
679        consts: &[],
680    },
681    Module {
682        name: "crypto",
683        funcs: &[
684            ModuleFn {
685                name: "sha256",
686                arity: 1,
687                optional: false,
688                runtime_fn: "crypto_sha256",
689                hint: "crypto.sha256(data)",
690            },
691            ModuleFn {
692                name: "hmac_sha256",
693                arity: 2,
694                optional: false,
695                runtime_fn: "crypto_hmac_sha256",
696                hint: "crypto.hmac_sha256(key, data)",
697            },
698            ModuleFn {
699                name: "token",
700                arity: 1,
701                optional: false,
702                runtime_fn: "crypto_token",
703                hint: "crypto.token(n)",
704            },
705            ModuleFn {
706                name: "same",
707                arity: 2,
708                optional: false,
709                runtime_fn: "crypto_same",
710                hint: "crypto.same(a, b)",
711            },
712        ],
713        consts: &[],
714    },
715];