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