Skip to main content

eventql_parser/
arena.rs

1use crate::typing::{ArgsRef, Record, Type, TypeRef};
2use crate::{Attrs, ExprKey, ExprPtr, ExprRef, Field, RecRef, StrRef, Value, VecRef};
3use rustc_hash::{FxBuildHasher, FxHashMap};
4use serde::Serialize;
5use std::collections::hash_map::Entry;
6use std::hash::BuildHasher;
7use unicase::Ascii;
8
9/// An arena-based allocator for interning strings.
10///
11/// Deduplicates strings by hash and returns lightweight [`StrRef`] handles for O(1) lookups.
12#[derive(Default, Serialize)]
13pub struct StringArena {
14    #[serde(skip_serializing)]
15    hasher: FxBuildHasher,
16
17    cache: FxHashMap<u64, StrRef>,
18    slots: Vec<String>,
19}
20
21impl StringArena {
22    /// Interns a string and returns its [`StrRef`]. Returns the existing reference if already interned.
23    pub fn alloc(&mut self, value: &str) -> StrRef {
24        match self.cache.entry(self.hasher.hash_one(value)) {
25            Entry::Occupied(entry) => *entry.get(),
26            Entry::Vacant(entry) => {
27                let key = StrRef(self.slots.len());
28                entry.insert(key);
29                self.slots.push(value.to_owned());
30
31                key
32            }
33        }
34    }
35
36    /// Interns a string using case-insensitive hashing.
37    ///
38    /// Two strings that differ only in ASCII case will resolve to the same [`StrRef`].
39    /// The original casing of the first insertion is preserved.
40    pub fn alloc_no_case(&mut self, value: &str) -> StrRef {
41        let hash = Ascii::new(value);
42        match self.cache.entry(self.hasher.hash_one(hash)) {
43            Entry::Occupied(entry) => *entry.get(),
44            Entry::Vacant(entry) => {
45                let key = StrRef(self.slots.len());
46                entry.insert(key);
47                self.slots.push(value.to_owned());
48
49                key
50            }
51        }
52    }
53
54    /// Retrieves the string associated with the given [`StrRef`].
55    pub fn get(&self, key: StrRef) -> &str {
56        &self.slots[key.0]
57    }
58
59    /// Compares two interned strings for case-insensitive ASCII equality.
60    pub fn eq_ignore_ascii_case(&self, ka: StrRef, kb: StrRef) -> bool {
61        self.get(ka).eq_ignore_ascii_case(self.get(kb))
62    }
63}
64
65/// An expression node stored in the [`ExprArena`].
66///
67/// Combines the expression's metadata ([`Attrs`]) with its actual content ([`Value`]).
68#[derive(Debug, Clone, Copy, Serialize)]
69pub struct Expr {
70    /// Metadata including source position.
71    pub attrs: Attrs,
72    /// The kind and content of this expression.
73    pub value: Value,
74}
75
76/// An arena-based allocator for EventQL expressions.
77///
78/// The `ExprArena` provides a memory-efficient way to store and manage AST nodes
79/// by using a flat vector and returning lightweight [`ExprRef`] handles.
80#[derive(Default, Serialize)]
81pub struct ExprArena {
82    #[serde(skip_serializing)]
83    hasher: FxBuildHasher,
84    exprs: Vec<Expr>,
85    vecs: Vec<Vec<ExprRef>>,
86    recs: Vec<Vec<Field>>,
87}
88
89impl ExprArena {
90    /// Allocates a new expression in the arena.
91    ///
92    /// This method takes an expression's attributes and value, hashes the value
93    /// to create a stable [`ExprKey`], and stores it in the arena. It returns
94    /// an [`ExprRef`] which can be used to retrieve the expression later.
95    pub fn alloc(&mut self, attrs: Attrs, value: Value) -> ExprRef {
96        let key = ExprKey(self.hasher.hash_one(value));
97
98        let ptr = ExprPtr(self.exprs.len());
99        self.exprs.push(Expr { attrs, value });
100
101        ExprRef { key, ptr }
102    }
103
104    /// Retrieves a node from the arena using an [`ExprRef`].
105    ///
106    /// # Panics
107    ///
108    /// Panics if the [`ExprRef`] contains an invalid pointer that is out of bounds
109    /// of the arena's internal storage.
110    pub fn get(&self, node_ref: ExprRef) -> Expr {
111        self.exprs[node_ref.ptr.0]
112    }
113
114    /// Allocates a vector of expression references and returns a [`VecRef`] handle.
115    pub fn alloc_vec(&mut self, values: Vec<ExprRef>) -> VecRef {
116        let key = VecRef(self.vecs.len());
117        self.vecs.push(values);
118
119        key
120    }
121
122    /// Allocates a vector of record fields and returns a [`RecRef`] handle.
123    pub fn alloc_rec(&mut self, values: Vec<Field>) -> RecRef {
124        let key = RecRef(self.recs.len());
125        self.recs.push(values);
126
127        key
128    }
129
130    /// Returns the slice of expression references for the given [`VecRef`].
131    pub fn vec(&self, ptr: VecRef) -> &[ExprRef] {
132        &self.vecs[ptr.0]
133    }
134
135    /// Returns the expression reference at index `idx` within the given [`VecRef`].
136    pub fn vec_get(&self, ptr: VecRef, idx: usize) -> ExprRef {
137        self.vecs[ptr.0][idx]
138    }
139
140    /// Returns an iterator over valid indices for the given [`VecRef`].
141    pub fn vec_idxes(&self, ptr: VecRef) -> impl Iterator<Item = usize> + use<> {
142        0..self.vec(ptr).len()
143    }
144
145    /// Returns the vector of fields for the given [`RecRef`].
146    pub fn rec(&self, ptr: RecRef) -> &Vec<Field> {
147        &self.recs[ptr.0]
148    }
149
150    /// Returns the field at index `idx` within the given [`RecRef`].
151    pub fn rec_get(&self, ptr: RecRef, idx: usize) -> Field {
152        self.recs[ptr.0][idx]
153    }
154
155    /// Returns an iterator over valid indices for the given [`RecRef`].
156    pub fn rec_idxes(&self, ptr: RecRef) -> impl Iterator<Item = usize> + use<> {
157        0..self.rec(ptr).len()
158    }
159}
160
161/// An arena-based allocator for type information.
162///
163/// Stores and deduplicates types, record definitions, and function argument lists.
164/// Supports freezing to mark a baseline and freeing types allocated after the baseline.
165#[derive(Default, Serialize)]
166pub struct TypeArena {
167    #[serde(skip_serializing)]
168    args_hasher: FxBuildHasher,
169
170    type_offset: usize,
171    rec_offset: usize,
172
173    dedup_types: FxHashMap<Type, TypeRef>,
174    dedup_args: FxHashMap<u64, ArgsRef>,
175    types: Vec<Type>,
176    pub(crate) records: Vec<FxHashMap<StrRef, Type>>,
177    pub(crate) args: Vec<Vec<Type>>,
178}
179
180impl TypeArena {
181    /// Marks the current allocation state as the baseline.
182    ///
183    /// Subsequent calls to [`free_space`](TypeArena::free_space) will deallocate
184    /// only types and records allocated after this point.
185    pub fn freeze(&mut self) {
186        self.rec_offset = self.records.len();
187        self.type_offset = self.types.len();
188    }
189
190    /// Frees types and records allocated after the last [`freeze`](TypeArena::freeze) call.
191    pub fn free_space(&mut self) {
192        for tpe in self.types.drain(self.type_offset..) {
193            self.dedup_types.remove(&tpe);
194        }
195
196        for _ in self.records.drain(self.rec_offset..) {}
197    }
198
199    /// Registers a type and returns a deduplicated [`TypeRef`]. Returns the existing reference if already registered.
200    pub fn register_type(&mut self, tpe: Type) -> TypeRef {
201        match self.dedup_types.entry(tpe) {
202            Entry::Occupied(entry) => *entry.get(),
203            Entry::Vacant(entry) => {
204                let key = TypeRef(self.types.len());
205                self.types.push(tpe);
206                entry.insert(key);
207
208                key
209            }
210        }
211    }
212
213    /// Allocates a fresh copy of a type. For records, this clones the record definition.
214    pub fn alloc_type(&mut self, tpe: Type) -> Type {
215        if let Type::Record(rec) = tpe {
216            let key = Record(self.records.len());
217            // TODO: technically, a deep-clone is needed here, where properties that point to
218            // records should also be allocated as well.
219            self.records.push(self.records[rec.0].clone());
220
221            return Type::Record(key);
222        }
223
224        tpe
225    }
226
227    /// Creates an array type containing elements of the given type.
228    pub fn alloc_array_of(&mut self, tpe: Type) -> Type {
229        Type::Array(self.register_type(tpe))
230    }
231
232    /// Allocates a new record type from a map of field names to types.
233    pub fn alloc_record(&mut self, record: FxHashMap<StrRef, Type>) -> Record {
234        let key = Record(self.records.len());
235        self.records.push(record);
236        key
237    }
238
239    /// Allocates a deduplicated list of function argument types and returns an [`ArgsRef`].
240    pub fn alloc_args(&mut self, args: &[Type]) -> ArgsRef {
241        let hash = self.args_hasher.hash_one(args);
242
243        match self.dedup_args.entry(hash) {
244            Entry::Occupied(entry) => *entry.get(),
245            Entry::Vacant(entry) => {
246                let key = ArgsRef(self.args.len());
247                entry.insert(key);
248                self.args.push(args.to_vec());
249
250                key
251            }
252        }
253    }
254
255    /// Retrieves the type for the given [`TypeRef`].
256    pub fn get_type(&self, key: TypeRef) -> Type {
257        self.types[key.0]
258    }
259
260    /// Returns the field map for the given record.
261    pub fn get_record(&self, key: Record) -> &FxHashMap<StrRef, Type> {
262        &self.records[key.0]
263    }
264
265    /// Returns the argument type slice for the given [`ArgsRef`].
266    pub fn get_args(&self, key: ArgsRef) -> &[Type] {
267        self.args[key.0].as_slice()
268    }
269
270    /// Returns a mutable reference to the argument type slice for the given [`ArgsRef`].
271    pub fn get_args_mut(&mut self, key: ArgsRef) -> &mut [Type] {
272        self.args[key.0].as_mut_slice()
273    }
274
275    /// Returns an iterator over valid indices for the given [`ArgsRef`].
276    pub fn args_idxes(&self, key: ArgsRef) -> impl Iterator<Item = usize> + use<> {
277        0..self.get_args(key).len()
278    }
279
280    /// Returns the argument type at index `idx` for the given [`ArgsRef`].
281    pub fn args_get(&self, key: ArgsRef, idx: usize) -> Type {
282        self.get_args(key)[idx]
283    }
284
285    /// Returns the type of a field in the given record, or `None` if the field doesn't exist.
286    pub fn record_get(&self, record: Record, field: StrRef) -> Option<Type> {
287        self.records[record.0].get(&field).copied()
288    }
289
290    /// Iterates over all (field name, type) pairs in the given record.
291    pub fn record_iter(&self, record: Record) -> impl Iterator<Item = (StrRef, Type)> {
292        self.records[record.0].iter().map(|(k, v)| (*k, *v))
293    }
294
295    /// Iterates over all field names in the given record.
296    pub fn record_keys(&self, record: Record) -> impl Iterator<Item = StrRef> {
297        self.records[record.0].keys().copied()
298    }
299
300    /// Checks whether two records have the exact same set of field names.
301    pub fn records_have_same_keys(&self, rec_a: Record, rec_b: Record) -> bool {
302        let rec_a = self.get_record(rec_a);
303        let rec_b = self.get_record(rec_b);
304
305        if rec_a.is_empty() && rec_b.is_empty() {
306            return true;
307        }
308
309        if rec_a.len() != rec_b.len() {
310            return false;
311        }
312
313        for bk in rec_b.keys() {
314            if !rec_a.contains_key(bk) {
315                return false;
316            }
317        }
318
319        true
320    }
321
322    /// Creates an empty record type.
323    pub fn instantiate_record(&mut self) -> Record {
324        self.alloc_record(FxHashMap::default())
325    }
326
327    /// Returns `true` if the given field exists in the record.
328    pub fn record_field_exists(&self, record: Record, field: StrRef) -> bool {
329        self.records[record.0].contains_key(&field)
330    }
331
332    /// Returns the hash map entry for a field in the given record, for in-place manipulation.
333    pub fn record_entry(&mut self, record: Record, key: StrRef) -> Entry<'_, StrRef, Type> {
334        self.records[record.0].entry(key)
335    }
336
337    /// Sets the type of a field in the given record, inserting or updating as needed.
338    pub fn record_set(&mut self, record: Record, field: StrRef, value: Type) {
339        self.records[record.0].insert(field, value);
340    }
341
342    /// Returns the number of fields in the given record.
343    pub fn record_len(&self, record: Record) -> usize {
344        self.records[record.0].len()
345    }
346
347    /// Returns `true` if the given record has no fields.
348    pub fn record_is_empty(&self, record: Record) -> bool {
349        self.records[record.0].is_empty()
350    }
351}
352
353/// Top-level arena that holds all memory pools for expressions, strings, and types.
354#[derive(Default, Serialize)]
355pub struct Arena {
356    pub(crate) exprs: ExprArena,
357    pub(crate) strings: StringArena,
358    pub(crate) types: TypeArena,
359}
360
361impl Arena {
362    /// Freezes the type arena to mark the current state as baseline.
363    pub fn freeze(&mut self) {
364        self.types.freeze();
365    }
366
367    /// Frees types allocated after the last freeze, reclaiming memory for reuse.
368    pub fn free_space(&mut self) {
369        self.types.free_space();
370    }
371}