Skip to main content

mangle_ir/
lib.rs

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Intermediate Representation (IR) for Mangle.
16//!
17//! This IR uses a flat, indexed representation (similar to Carbon's SemIR).
18//! Instructions are stored in a vector and referenced by `InstId`.
19//!
20//! # Relation Representation
21//!
22//! Relations (predicates) are primarily identified by `NameId`.
23//! In the Logical IR, they appear in `Inst::Atom` and `Inst::Decl`.
24//!
25//! In the Physical IR (`physical::Op`), relations are abstract data sources.
26//! Operations like `Scan` and `Insert` refer to relations by name/ID, but the
27//! actual storage format (row-oriented, column-oriented, B-Tree, etc.) is
28//! determined by the runtime `Host` implementation.
29//!
30//! # Physical Operations
31//!
32//! The `physical` module defines the imperative operations for execution:
33//!
34//! *   **Iterate/Scan**: Provides an iterator over a relation.
35//! *   **Filter**: Selects tuples matching a condition.
36//! *   **Insert**: Adds derived facts to a relation.
37//! *   **Let**: Binds values to variables for projection or calculation.
38//!
39//! The Planner transforms declarative rules into trees of these operations.
40
41pub mod physical;
42
43use std::collections::HashMap;
44use std::hash::Hash;
45use std::num::NonZeroU32;
46
47/// Index of an instruction in the IR.
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
49pub struct InstId(pub NonZeroU32);
50
51impl InstId {
52    pub fn new(index: usize) -> Self {
53        // We use 0-based index internally, but 1-based NonZeroU32 storage
54        // to allow Option<InstId> to be same size as InstId.
55        // index 0 -> 1
56        InstId(NonZeroU32::new((index + 1) as u32).expect("index overflow"))
57    }
58
59    pub fn index(&self) -> usize {
60        (self.0.get() - 1) as usize
61    }
62}
63
64pub trait InternKey: Copy + Eq + Hash {
65    fn new(index: usize) -> Self;
66    fn index(&self) -> usize;
67}
68
69/// Index of a name (identifier) in the IR.
70#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
71pub struct NameId(pub NonZeroU32);
72
73impl InternKey for NameId {
74    fn new(index: usize) -> Self {
75        NameId(NonZeroU32::new((index + 1) as u32).expect("index overflow"))
76    }
77
78    fn index(&self) -> usize {
79        (self.0.get() - 1) as usize
80    }
81}
82
83/// Index of a string constant in the IR.
84#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
85pub struct StringId(pub NonZeroU32);
86
87impl InternKey for StringId {
88    fn new(index: usize) -> Self {
89        StringId(NonZeroU32::new((index + 1) as u32).expect("index overflow"))
90    }
91
92    fn index(&self) -> usize {
93        (self.0.get() - 1) as usize
94    }
95}
96
97/// A simple interner for strings.
98#[derive(Debug)]
99pub struct Store<K: InternKey> {
100    map: HashMap<String, K>,
101    vec: Vec<String>,
102}
103
104impl<K: InternKey> Default for Store<K> {
105    fn default() -> Self {
106        Self {
107            map: HashMap::default(),
108            vec: Vec::new(),
109        }
110    }
111}
112
113impl<K: InternKey> Store<K> {
114    pub fn new() -> Self {
115        Self::default()
116    }
117
118    pub fn intern(&mut self, name: String) -> K {
119        if let Some(&id) = self.map.get(&name) {
120            return id;
121        }
122        let id = K::new(self.vec.len());
123        self.vec.push(name.clone());
124        self.map.insert(name, id);
125        id
126    }
127
128    pub fn get(&self, id: K) -> &str {
129        &self.vec[id.index()]
130    }
131
132    pub fn lookup(&self, name: &str) -> Option<K> {
133        self.map.get(name).copied()
134    }
135}
136
137/// The IR container.
138#[derive(Default, Debug)]
139pub struct Ir {
140    pub insts: Vec<Inst>,
141    pub name_store: Store<NameId>,
142    pub string_store: Store<StringId>,
143}
144
145impl Ir {
146    pub fn new() -> Self {
147        Self::default()
148    }
149
150    pub fn add_inst(&mut self, inst: Inst) -> InstId {
151        let id = InstId::new(self.insts.len());
152        self.insts.push(inst);
153        id
154    }
155
156    pub fn get(&self, id: InstId) -> &Inst {
157        &self.insts[id.index()]
158    }
159
160    pub fn intern_name(&mut self, name: impl Into<String>) -> NameId {
161        self.name_store.intern(name.into())
162    }
163
164    pub fn resolve_name(&self, id: NameId) -> &str {
165        self.name_store.get(id)
166    }
167
168    pub fn intern_string(&mut self, s: impl Into<String>) -> StringId {
169        self.string_store.intern(s.into())
170    }
171
172    pub fn resolve_string(&self, id: StringId) -> &str {
173        self.string_store.get(id)
174    }
175}
176
177/// Instructions in the Mangle IR.
178#[derive(Clone, Debug, PartialEq)]
179pub enum Inst {
180    // --- Constants ---
181    Bool(bool),
182    Number(i64),
183    Float(f64),
184    /// A string constant (e.g. "foo").
185    String(StringId),
186    Bytes(Vec<u8>),
187    /// A name constant (e.g. /foo).
188    Name(NameId),
189
190    /// A list of values. args point to other Constant-like instructions.
191    List(Vec<InstId>),
192    /// A map of values. keys and values must have same length.
193    Map {
194        keys: Vec<InstId>,
195        values: Vec<InstId>,
196    },
197    /// A struct. fields and values must have same length.
198    Struct {
199        fields: Vec<NameId>,
200        values: Vec<InstId>,
201    },
202
203    // --- Variables ---
204    /// A variable.
205    Var(NameId),
206
207    // --- Expressions (BaseTerm) ---
208    /// Application of a function.
209    ApplyFn {
210        function: NameId,
211        args: Vec<InstId>,
212    },
213
214    // --- Logical Formulas (Term / Atom) ---
215    /// An atom (predicate application).
216    Atom {
217        predicate: NameId,
218        args: Vec<InstId>,
219    },
220    /// Negation of an atom.
221    NegAtom(InstId),
222    /// Equality constraint (left = right).
223    Eq(InstId, InstId),
224    /// Inequality constraint (left != right).
225    Ineq(InstId, InstId),
226
227    // --- Transforms ---
228    /// A transform statement: let var = app.
229    Transform {
230        var: Option<NameId>,
231        app: InstId,
232    },
233
234    // --- Structure (Clauses / Decls) ---
235    /// A Horn clause (Rule).
236    Rule {
237        head: InstId,           // Points to Atom
238        premises: Vec<InstId>,  // Points to Atom, NegAtom, Eq, Ineq
239        transform: Vec<InstId>, // Points to Transform
240    },
241
242    /// A Declaration.
243    Decl {
244        atom: InstId,
245        descr: Vec<InstId>,          // Atoms
246        bounds: Vec<InstId>,         // BoundDecls
247        constraints: Option<InstId>, // Constraints
248    },
249
250    /// Bound Declaration.
251    BoundDecl {
252        base_terms: Vec<InstId>,
253    },
254
255    /// Constraints.
256    Constraints {
257        consequences: Vec<InstId>,      // Atoms
258        alternatives: Vec<Vec<InstId>>, // List of List of Atoms
259    },
260}
261
262#[cfg(test)]
263mod compat_test;
264
265#[cfg(test)]
266mod tests {
267    use super::*;
268
269    #[test]
270    fn basic_ir_construction() {
271        let mut ir = Ir::new();
272
273        // p(X) :- q(X).
274
275        let x_name = ir.intern_name("X");
276        let var_x = ir.add_inst(Inst::Var(x_name));
277
278        let p_name = ir.intern_name("p");
279        let atom_head = ir.add_inst(Inst::Atom {
280            predicate: p_name,
281            args: vec![var_x],
282        });
283
284        let q_name = ir.intern_name("q");
285        let atom_body = ir.add_inst(Inst::Atom {
286            predicate: q_name,
287            args: vec![var_x],
288        });
289
290        let rule = ir.add_inst(Inst::Rule {
291            head: atom_head,
292            premises: vec![atom_body],
293            transform: vec![],
294        });
295
296        assert_eq!(ir.insts.len(), 4);
297
298        if let Inst::Rule { head, .. } = ir.get(rule) {
299            assert_eq!(*head, atom_head);
300        } else {
301            panic!("Expected Rule");
302        }
303    }
304
305    #[test]
306    fn complex_types() {
307        let mut ir = Ir::new();
308
309        // Model: .Pair</string, .Struct<opt /a : /string>>
310
311        // Constants / Symbols
312
313        let n_string = ir.intern_name("/string");
314        let s_string = ir.add_inst(Inst::Name(n_string));
315
316        let n_a = ir.intern_name("/a");
317        let s_a = ir.add_inst(Inst::Name(n_a));
318
319        // Struct field type: opt /a : /string
320
321        // In Mangle Go AST, this is ApplyFn("fn:opt", [/a, /string])
322
323        let fn_opt_name = ir.intern_name("fn:opt");
324        let fn_opt = ir.add_inst(Inst::ApplyFn {
325            function: fn_opt_name,
326            args: vec![s_a, s_string],
327        });
328
329        // Struct type: .Struct<...>
330
331        // ApplyFn("fn:Struct", [fn_opt])
332
333        let fn_struct_name = ir.intern_name("fn:Struct");
334        let fn_struct = ir.add_inst(Inst::ApplyFn {
335            function: fn_struct_name,
336            args: vec![fn_opt],
337        });
338
339        // Pair type: .Pair</string, Struct...>
340
341        let fn_pair_name = ir.intern_name("fn:Pair");
342        let fn_pair = ir.add_inst(Inst::ApplyFn {
343            function: fn_pair_name,
344            args: vec![s_string, fn_struct],
345        });
346
347        // Check structure
348
349        if let Inst::ApplyFn { function, args } = ir.get(fn_pair) {
350            assert_eq!(ir.resolve_name(*function), "fn:Pair");
351
352            assert_eq!(args.len(), 2);
353
354            assert_eq!(args[0], s_string);
355
356            assert_eq!(args[1], fn_struct);
357        } else {
358            panic!("Expected ApplyFn");
359        }
360    }
361}