beck_core/
stdlib.rs

1//! The standard library's Beck half, carried inside the compiler.
2//!
3//! [`compiler/lib/README.md`](../../../../lib/README.md) divides the standard library in two: a
4//! host's table or grammar is a primitive in [`crate::prelude`], and composition is a file written
5//! in Beck. The primitive half has always been in the binary. This is the other half, in the binary
6//! for the same reason — so that `import bignum` means the library this compiler was built with,
7//! wherever the program being compiled happens to sit.
8//!
9//! [`adr/0018`](../../../../../docs/adr/0018-the-standard-library-is-carried-in-the-compiler.md) is
10//! the decision and the alternatives it was taken over; [`10`](../../../../../docs/10-decisions.md)
11//! D23 is the language-level rule:
12//!
13//! > `import x` resolves against the root module's own directory first, and against the standard
14//! > library second.
15//!
16//! The directory-first half is what keeps the files here editable: `lib/decimal.beck` imports
17//! `bignum` and gets the file beside it, so working on the library does not mean rebuilding the
18//! compiler to see the change. A program anywhere else gets this copy.
19
20use crate::project::Sources;
21
22/// Every module in `compiler/lib/`, under the name an `import` writes.
23///
24/// The list is here rather than discovered by a `build.rs` walk because a file appearing in the
25/// standard library is an API addition — a new name every program in the language can suddenly
26/// import — and one that should be written down and reviewed rather than picked up. It is not
27/// trusted to be complete: `beck-cli/tests/stdlib.rs` reads the directory and fails if a file there
28/// is missing from here, which is the same gate the directory has always had for its tests.
29pub const MODULES: &[(&str, &str)] = &[
30    ("bignum", include_str!("../../../lib/bignum.beck")),
31    ("collections", include_str!("../../../lib/collections.beck")),
32    ("crypto", include_str!("../../../lib/crypto.beck")),
33    ("dates", include_str!("../../../lib/dates.beck")),
34    ("decimal", include_str!("../../../lib/decimal.beck")),
35    ("documents", include_str!("../../../lib/documents.beck")),
36    ("format", include_str!("../../../lib/format.beck")),
37    ("http", include_str!("../../../lib/http.beck")),
38    ("money", include_str!("../../../lib/money.beck")),
39    ("text", include_str!("../../../lib/text.beck")),
40];
41
42/// The source of a standard-library module, if there is one by that name.
43pub fn source(name: &str) -> Option<&'static str> {
44    MODULES.iter().find(|(n, _)| *n == name).map(|(_, s)| *s)
45}
46
47/// Whether `name` is a standard-library module.
48pub fn has(name: &str) -> bool {
49    source(name).is_some()
50}
51
52/// Every name a program can import without a file beside it.
53pub fn names() -> impl Iterator<Item = &'static str> {
54    MODULES.iter().map(|(n, _)| *n)
55}
56
57/// The module as the project loader wants it.
58///
59/// The path is `<std>/<name>.beck` rather than a filesystem path, because there is no file: a
60/// diagnostic inside a standard-library module has to say where it is, and saying `lib/bignum.beck`
61/// would name a file that need not exist on the machine compiling.
62pub fn sources(name: &str) -> Option<Sources> {
63    source(name).map(|text| Sources {
64        module: Some(text.to_string()),
65        interface: None,
66        path: Some(format!("<std>/{name}.beck")),
67    })
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn the_table_is_sorted_and_has_no_duplicates() {
76        let names: Vec<&str> = names().collect();
77        let mut sorted = names.clone();
78        sorted.sort_unstable();
79        sorted.dedup();
80        assert_eq!(
81            names, sorted,
82            "the standard-library table is not a sorted set"
83        );
84    }
85
86    #[test]
87    fn every_module_carries_its_source() {
88        for (name, text) in MODULES {
89            assert!(!text.trim().is_empty(), "`{name}` is embedded empty");
90        }
91    }
92
93    /// A module a program does not have beside it is not a standard-library module by accident.
94    #[test]
95    fn a_name_that_is_not_in_the_library_resolves_to_nothing() {
96        assert!(source("nowhere").is_none());
97        assert!(!has("prelude"));
98    }
99}