preserves_schema/compiler/
context.rs

1use crate::gen::schema::*;
2use crate::syntax::block::constructors::*;
3use crate::syntax::block::escape_string;
4use crate::syntax::block::Item;
5use crate::*;
6
7use convert_case::{Case, Casing};
8
9use lazy_static::lazy_static;
10
11use preserves::value::IOValue;
12use preserves::value::Map;
13use preserves::value::NestedValue;
14use preserves::value::Value;
15
16use super::names;
17use super::types;
18use super::types::Purpose;
19use super::CompilerConfig;
20
21pub struct BundleContext<'b> {
22    pub config: &'b CompilerConfig,
23    pub types: Map<Ref, types::TDefinition>,
24    pub literals: Map<IOValue, String>,
25}
26
27#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
28pub enum ModuleContextMode {
29    TargetModule,
30    TargetToplevel,
31    TargetGeneric,
32}
33
34pub struct ModuleContext<'m, 'b> {
35    pub bundle: &'m mut BundleContext<'b>,
36    pub module_path: ModulePath,
37    pub schema: &'m Schema,
38    pub typedefs: Vec<Item>,
39    pub functiondefs: Vec<Item>,
40    pub mode: ModuleContextMode,
41}
42
43pub struct FunctionContext<'a, 'm, 'b> {
44    pub error_context: String,
45    pub m: &'a mut ModuleContext<'m, 'b>,
46    pub temp_counter: usize,
47    pub captures: Vec<Capture>,
48    pub capture_mode: CaptureMode,
49}
50
51pub struct Capture {
52    pub field_name: String,
53    pub ty: types::TField,
54    pub source_expr: String,
55}
56
57pub enum CaptureMode {
58    Definite,
59    Indefinite(Vec<Item>),
60}
61
62pub enum RefRenderStyle {
63    Bare,
64    Qualified,
65}
66
67lazy_static! {
68    static ref ID_RE: regex::Regex = regex::Regex::new(r"^[a-zA-Z][a-zA-Z_0-9]*$").unwrap();
69}
70
71impl<'b> BundleContext<'b> {
72    pub fn new(config: &'b CompilerConfig) -> Self {
73        BundleContext {
74            config,
75            types: config.build_type_cache(),
76            literals: Map::new(),
77        }
78    }
79
80    pub fn any_type(&self) -> &'static str {
81        "_Value"
82    }
83
84    pub fn lookup_definition(&self, r: &Ref) -> Option<(&Definition, Purpose)> {
85        self.config
86            .bundle
87            .get(&r.module.0)
88            .and_then(|s| s.0.definitions.0.get(&r.name).map(|d| (d, s.1)))
89    }
90
91    pub fn type_for_name(&self, r: &Ref) -> Option<&types::TDefinition> {
92        if r.module.0.is_empty() {
93            panic!(
94                "BundleContext::type_for_name with module-relative ref {:?}",
95                r
96            );
97        }
98        let result = self.types.get(r);
99        if result.is_none() && !self.config.external_modules.contains_key(&r.module.0) {
100            panic!("Attempted to lookup unknown type {:?}", r)
101        }
102        result
103    }
104
105    pub fn define_literal(&mut self, v: &IOValue) -> String {
106        let prefix = format!("LIT_{}", self.literals.len());
107        let next_id = match v.value() {
108            Value::Boolean(b) => prefix + "_" + &b.to_string(),
109            Value::Symbol(s) => {
110                if ID_RE.is_match(&s) {
111                    prefix + "_" + s
112                } else {
113                    prefix
114                }
115            }
116            Value::String(s) => {
117                if ID_RE.is_match(&s) {
118                    prefix + "_" + s
119                } else {
120                    prefix
121                }
122            }
123            Value::SignedInteger(n) => prefix + "_" + &n.to_string(),
124            _ => prefix,
125        };
126        let next_id = next_id.to_case(Case::UpperSnake);
127        format!(
128            "&<_L as Into<&'a {}>>::into(_ctxt).{}",
129            self.language_type(),
130            self.literals.entry(v.clone()).or_insert(next_id)
131        )
132    }
133
134    pub fn generate_module<F: FnOnce(&mut ModuleContext)>(
135        &mut self,
136        path: &Vec<String>,
137        schema: &Schema,
138        mode: ModuleContextMode,
139        items: &mut Map<ModuleContextMode, Vec<Item>>,
140        f: F,
141    ) {
142        let mut m = ModuleContext::new(self, &ModulePath(path.clone()), schema, mode);
143        f(&mut m);
144        items.entry(mode).or_default().extend(m.extract());
145    }
146
147    pub fn language_struct_name(&self) -> &'static str {
148        "Language"
149    }
150
151    pub fn language_type_base(&self) -> String {
152        format!(
153            "{}::{}",
154            self.config.fully_qualified_module_prefix.clone(),
155            self.language_struct_name()
156        )
157    }
158
159    pub fn language_type(&self) -> String {
160        format!("{}<{}>", self.language_type_base(), self.any_type())
161    }
162}
163
164impl<'m, 'b> ModuleContext<'m, 'b> {
165    pub fn new(
166        bundle: &'m mut BundleContext<'b>,
167        module_path: &ModulePath,
168        schema: &'m Schema,
169        mode: ModuleContextMode,
170    ) -> Self {
171        ModuleContext {
172            bundle,
173            module_path: module_path.to_owned(),
174            schema,
175            typedefs: Vec::new(),
176            functiondefs: Vec::new(),
177            mode,
178        }
179    }
180
181    pub fn any_type(&self) -> &'static str {
182        self.bundle.any_type()
183    }
184
185    pub fn reset_mode(&mut self) {
186        self.mode = ModuleContextMode::TargetToplevel;
187    }
188
189    pub fn define_literal(&mut self, v: &IOValue) -> String {
190        self.bundle.define_literal(v)
191    }
192
193    pub fn define_type(&mut self, i: Item) {
194        self.typedefs.push(i)
195    }
196
197    pub fn define_function<F: FnOnce(FunctionContext) -> Item>(
198        &mut self,
199        error_context: &str,
200        f: F,
201    ) {
202        let i = f(FunctionContext::new(self, error_context));
203        self.functiondefs.push(i)
204    }
205
206    pub fn render_ref(&self, r: &Ref, style: RefRenderStyle) -> Item {
207        let base = match self.bundle.config.external_modules.get(&r.module.0) {
208            None => {
209                if r.module.0.is_empty() {
210                    item(names::render_constructor(&r.name))
211                } else {
212                    let mut items = Vec::new();
213                    items.push(item(
214                        self.bundle.config.fully_qualified_module_prefix.to_owned(),
215                    ));
216                    for p in &r.module.0 {
217                        items.push(item(names::render_modname(p)))
218                    }
219                    items.push(item(names::render_constructor(&r.name)));
220                    item(name(items))
221                }
222            }
223            Some(xm) => item(name![
224                xm.rust_namespace.clone(),
225                names::render_constructor(&r.name)
226            ]),
227        };
228        let q = self.ref_has_embedded(r);
229        match style {
230            RefRenderStyle::Bare => base,
231            RefRenderStyle::Qualified => {
232                if q {
233                    item(seq![base, anglebrackets![self.any_type()]])
234                } else {
235                    base
236                }
237            }
238        }
239    }
240
241    pub fn ref_has_embedded(&self, r: &Ref) -> bool {
242        let r = r.qualify(&self.module_path);
243        self.bundle
244            .type_for_name(&r)
245            .map(|ty| ty.has_embedded(self.bundle))
246            .unwrap_or(false)
247        // ^ TODO: should the "false" be configurable?
248    }
249
250    pub fn parse_unparse_generic_decls(&self, ty: &types::TDefinition) -> Item {
251        let mut lts = ty.language_types(self.bundle);
252        lts.insert(self.bundle.language_type());
253        item(anglebrackets![
254            "'a",
255            seq![
256                "_L: Copy",
257                seq(lts
258                    .into_iter()
259                    .map(|t| item(seq![" + Into<&'a ", t, ">"]))
260                    .collect())
261            ],
262            seq![self.any_type(), ": preserves::value::NestedValue + 'a"]
263        ])
264    }
265
266    pub fn extract(&mut self) -> Vec<Item> {
267        let mut items = std::mem::take(&mut self.typedefs);
268        items.extend(std::mem::take(&mut self.functiondefs));
269        items
270    }
271}
272
273impl<'a, 'm, 'b> FunctionContext<'a, 'm, 'b> {
274    pub fn new(m: &'a mut ModuleContext<'m, 'b>, error_context: &str) -> Self {
275        FunctionContext {
276            error_context: error_context.to_owned(),
277            m,
278            temp_counter: 0,
279            captures: Vec::new(),
280            capture_mode: CaptureMode::Definite,
281        }
282    }
283
284    pub fn capture(&mut self, field_name: String, ty: types::TField, source_expr: String) {
285        self.captures.push(Capture {
286            field_name,
287            ty,
288            source_expr: match self.capture_mode {
289                CaptureMode::Definite => source_expr,
290                CaptureMode::Indefinite(_) => format!(
291                    "{}.ok_or_else(|| {:?})?",
292                    source_expr,
293                    self.conformance_err_code()
294                ),
295            },
296        })
297    }
298
299    pub fn lookup_capture(&self, field_name: &str) -> &Capture {
300        for c in &self.captures {
301            if c.field_name == field_name {
302                return c;
303            }
304        }
305        panic!("No capture for field {:?} available", field_name)
306    }
307
308    pub fn gentempname(&mut self) -> String {
309        let i = self.temp_counter;
310        self.temp_counter += 1;
311        format!("_tmp{}", i)
312    }
313
314    pub fn declare_compound(&self, body: &mut Vec<Item>, name: &str, init_expr: Item) {
315        body.push(item(seq![
316            "let mut ",
317            name.to_owned(),
318            " = ",
319            init_expr,
320            ";"
321        ]));
322    }
323
324    pub fn define_atom(&mut self, body: &mut Vec<Item>, name: &str, val_expr: Item) {
325        body.push(match &mut self.capture_mode {
326            CaptureMode::Definite => item(seq!["let ", name.to_owned(), " = ", val_expr, ";"]),
327            CaptureMode::Indefinite(items) => {
328                items.push(item(seq!["let mut ", name.to_owned(), " = None;"]));
329                item(seq![name.to_owned(), " = Some(", val_expr, ");"])
330            }
331        })
332    }
333
334    pub fn with_definite_mode<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R {
335        let saved_mode = std::mem::replace(&mut self.capture_mode, CaptureMode::Definite);
336        let result = f(self);
337        match std::mem::replace(&mut self.capture_mode, saved_mode) {
338            CaptureMode::Definite => (),
339            CaptureMode::Indefinite(_) => panic!("corrupt capture_mode"),
340        }
341        result
342    }
343
344    pub fn with_indefinite_mode<F: FnOnce(&mut Self) -> ()>(&mut self, f: F) -> Vec<Item> {
345        let saved_mode =
346            std::mem::replace(&mut self.capture_mode, CaptureMode::Indefinite(Vec::new()));
347        f(self);
348        match std::mem::replace(&mut self.capture_mode, saved_mode) {
349            CaptureMode::Definite => panic!("corrupt capture_mode"),
350            CaptureMode::Indefinite(declarations) => declarations,
351        }
352    }
353
354    pub fn branch<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R {
355        let saved_temp_counter = self.temp_counter;
356        let saved_capture_count = self.captures.len();
357        let result = f(self);
358        self.temp_counter = saved_temp_counter;
359        self.captures.truncate(saved_capture_count);
360        result
361    }
362
363    pub fn err_code(&self) -> Item {
364        return item(seq!["Err", parens![self.conformance_err_code()]]);
365    }
366
367    pub fn fully_qualified_error_context(&self) -> String {
368        self.m.module_path.0.join(".") + "." + &self.error_context
369    }
370
371    pub fn conformance_err_code(&self) -> Item {
372        return item(seq![
373            "_support::ParseError::conformance_error",
374            parens![escape_string(&self.fully_qualified_error_context())]
375        ]);
376    }
377}