rustre_core/engine/
world.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use crate::engine::FileError;
use comemo::Prehashed;
use ecow::{EcoString, EcoVec};
use rustre_parser::ast::Root;
use rustre_parser::ParserError;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::path::Path;
use std::rc::Rc;

#[track]
pub trait World {
    /// [Source]s of the compilation entry point(s)
    ///
    /// A Rustre command can usually be invoked with multiple source files, in such a way that they
    /// would all be loaded in the same environment. This method should return them all. Keep in
    /// mind that this method will only return files that were explicitly loaded as input files,
    /// this means that files indirectly loaded using `#include` won't be part of this list. Use the
    /// [`crate::all_sources`] query to get an exhaustive list of all files loaded directly and
    /// indirectly.
    fn input_files(&self) -> EcoVec<Source>;

    /// Parsed contents of a Lustre file
    fn imported_file(&self, relative_to: &Source, path: &Path) -> Result<Source, FileError>;
}

/// Identifies a file
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct FileId(Prehashed<FileIdInner>);

#[derive(Clone, Eq, Hash, PartialEq)]
enum FileIdInner {
    Normal(Rc<Path>),
    StdLib,
}

impl Debug for FileId {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        if self.is_stdlib() {
            write!(f, "<standard library>")
        } else {
            self.path().fmt(f)
        }
    }
}

impl FileId {
    /// Creates a [FileId] for the given [Path]
    #[track_caller]
    pub fn new(path: impl AsRef<Path>) -> Self {
        Self(Prehashed::new(FileIdInner::Normal(
            path.as_ref().to_owned().into(),
        )))
    }

    /// Returns the unique "magic" [FileId] that corresponds to the Lustre standard library
    pub fn stdlib() -> Self {
        Self(Prehashed::new(FileIdInner::StdLib))
    }

    pub fn is_stdlib(&self) -> bool {
        matches!(&*self.0, FileIdInner::StdLib)
    }

    pub fn path(&self) -> &Path {
        if let FileIdInner::Normal(ref path) = &*self.0 {
            path
        } else {
            // Dummy path for the standard library
            Path::new("/@stdlib.lus")
        }
    }
}

#[derive(Clone)]
pub struct Source(Rc<SourceImpl>);

impl Eq for Source {}
impl PartialEq for Source {
    fn eq(&self, other: &Self) -> bool {
        Rc::ptr_eq(&self.0, &other.0)
    }
}

impl Hash for Source {
    fn hash<H: Hasher>(&self, state: &mut H) {
        std::ptr::hash(Rc::as_ptr(&self.0), state);
    }
}

struct SourceImpl {
    id: FileId,
    raw: Prehashed<EcoString>,
    syntax: Prehashed<Root>,
    syntax_errors: EcoVec<ParserError>,
}

impl Debug for Source {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Source")
            .field("id", &self.0.id)
            .finish_non_exhaustive()
    }
}

impl Hash for SourceImpl {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id.hash(state);
        self.raw.hash(state);
        self.syntax.hash(state);
    }
}

impl AsRef<str> for Source {
    fn as_ref(&self) -> &str {
        self.0.raw.as_str()
    }
}

impl AsRef<[u8]> for Source {
    fn as_ref(&self) -> &[u8] {
        self.0.raw.as_bytes()
    }
}

impl Source {
    pub fn new(id: FileId, text: String) -> Self {
        let (root, syntax_errors) = rustre_parser::parse(&text);
        Self(Rc::new(SourceImpl {
            id,
            raw: Prehashed::new(text.into()),
            syntax: Prehashed::new(root),
            syntax_errors: syntax_errors.into(),
        }))
    }

    pub fn stdlib() -> Self {
        Source::new(FileId::stdlib(), include_str!("../stdlib.lus").into())
    }

    pub fn file(&self) -> FileId {
        self.0.id.clone()
    }

    pub fn root(&self) -> Root {
        self.0.syntax.clone().into_inner()
    }

    pub fn syntax_errors(&self) -> EcoVec<ParserError> {
        self.0.syntax_errors.clone()
    }
}