tblgen/
record.rs

1// Original work Copyright 2016 Alexander Stocko <as@coder.gg>.
2// Modified work Copyright 2023 Daan Vanoverloop
3// See the COPYRIGHT file at the top-level directory of this distribution.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11use paste::paste;
12use std::{ffi::c_void, marker::PhantomData};
13
14use crate::raw::{
15    TableGenRecordRef, TableGenRecordValRef, tableGenRecordGetFirstValue, tableGenRecordGetLoc,
16    tableGenRecordGetName, tableGenRecordGetValue, tableGenRecordIsAnonymous,
17    tableGenRecordIsSubclassOf, tableGenRecordPrint, tableGenRecordValGetLoc,
18    tableGenRecordValGetNameInit, tableGenRecordValGetValue, tableGenRecordValNext,
19    tableGenRecordValPrint,
20};
21
22use crate::{
23    error::{Error, SourceLoc, SourceLocation, TableGenError, WithLocation},
24    init::{BitInit, DagInit, ListInit, StringInit, TypedInit},
25    string_ref::StringRef,
26    util::print_callback,
27};
28use std::fmt::{self, Debug, Display, Formatter};
29
30/// An immutable reference to a TableGen record.
31///
32/// This reference cannot outlive the
33/// [`RecordKeeper`](crate::record_keeper::RecordKeeper) from which it is
34/// borrowed.
35#[derive(Clone, Copy, PartialEq, Eq)]
36pub struct Record<'a> {
37    raw: TableGenRecordRef,
38    _reference: PhantomData<&'a TableGenRecordRef>,
39}
40
41impl Display for Record<'_> {
42    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
43        let mut data = (formatter, Ok(()));
44
45        unsafe {
46            tableGenRecordPrint(
47                self.raw,
48                Some(print_callback),
49                &mut data as *mut _ as *mut c_void,
50            );
51        }
52
53        data.1
54    }
55}
56
57impl Debug for Record<'_> {
58    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
59        writeln!(formatter, "Record(")?;
60        Display::fmt(self, formatter)?;
61        write!(formatter, ")")
62    }
63}
64
65macro_rules! record_value {
66    ($(#[$attr:meta])* $name:ident, $type:ty) => {
67        paste! {
68            $(#[$attr])*
69            pub fn [<$name _value>](self, name: &str) -> Result<$type, Error> {
70                self.value(name)?.try_into()
71            }
72        }
73    };
74}
75
76impl<'a> Record<'a> {
77    /// Creates a record from a raw object.
78    ///
79    /// # Safety
80    ///
81    /// The raw object must be valid.
82    pub unsafe fn from_raw(ptr: TableGenRecordRef) -> Record<'a> {
83        Record {
84            raw: ptr,
85            _reference: PhantomData,
86        }
87    }
88
89    /// Returns the name of the record.
90    ///
91    /// # Errors
92    ///
93    /// Returns an error if the name is not a valid UTF-8 string.
94    pub fn name(self) -> Result<&'a str, Error> {
95        unsafe { StringRef::from_raw(tableGenRecordGetName(self.raw)) }
96            .try_into()
97            .map_err(TableGenError::from)
98            .map_err(|e| e.with_location(self))
99    }
100
101    record_value!(
102        /// Returns the boolean value of the field with the given name if this
103        /// field is of type [`BitInit`](crate::init::BitInit).
104        bit,
105        bool
106    );
107    record_value!(
108        /// Returns the field with the given name converted to a [`Vec<bool>`]
109        /// if this field is of type [`BitsInit`](crate::init::BitsInit).
110        bits,
111        Vec<bool>
112    );
113    record_value!(
114        /// Returns the integer value of the field with the given name if this
115        /// field is of type [`IntInit`](crate::init::IntInit).
116        int,
117        i64
118    );
119    record_value!(
120        /// Returns the field with the given name converted to a [`String`]
121        /// if this field is of type [`StringInit`](crate::init::StringInit).
122        ///
123        /// Note that this copies the string into a new string.
124        code,
125        String
126    );
127    record_value!(
128        /// Returns the field with the given name converted to a [`&str`]
129        /// if this field is of type [`StringInit`](crate::init::StringInit).
130        code_str,
131        &'a str
132    );
133    record_value!(
134        /// Returns the field with the given name converted to a [`String`]
135        /// if this field is of type [`StringInit`](crate::init::StringInit).
136        ///
137        /// Note that this copies the string into a new string.
138        string,
139        String
140    );
141    record_value!(
142        /// Returns the field with the given name converted to a [`&str`]
143        /// if this field is of type [`StringInit`](crate::init::StringInit).
144        str,
145        &'a str
146    );
147    record_value!(
148        /// Returns the field with the given name converted to a [`Record`]
149        /// if this field is of type [`DefInit`](crate::init::DefInit).
150        def,
151        Record<'a>
152    );
153    record_value!(
154        /// Returns the field with the given name converted to a [`ListInit`]
155        /// if this field is of type [`ListInit`].
156        list,
157        ListInit<'a>
158    );
159    record_value!(
160        /// Returns the field with the given name converted to a [`DagInit`]
161        /// if this field is of type [`DagInit`].
162        dag,
163        DagInit<'a>
164    );
165
166    /// Returns a [`RecordValue`] for the field with the given name.
167    pub fn value<'n>(self, name: &'n str) -> Result<RecordValue<'a>, Error> {
168        let value = unsafe { tableGenRecordGetValue(self.raw, StringRef::from(name).to_raw()) };
169        if !value.is_null() {
170            Ok(unsafe { RecordValue::from_raw(value) })
171        } else {
172            Err(TableGenError::MissingValue(String::from(name)).with_location(self))
173        }
174    }
175
176    /// Returns true if the record is anonymous.
177    pub fn anonymous(self) -> bool {
178        unsafe { tableGenRecordIsAnonymous(self.raw) > 0 }
179    }
180
181    /// Returns true if the record is a subclass of the class with the given
182    /// name.
183    pub fn subclass_of(self, class: &str) -> bool {
184        unsafe { tableGenRecordIsSubclassOf(self.raw, StringRef::from(class).to_raw()) > 0 }
185    }
186
187    /// Returns an iterator over the fields of the record.
188    ///
189    /// The iterator yields [`RecordValue`] structs
190    pub fn values(self) -> RecordValueIter<'a> {
191        RecordValueIter::new(self)
192    }
193}
194
195impl SourceLoc for Record<'_> {
196    fn source_location(self) -> SourceLocation {
197        unsafe { SourceLocation::from_raw(tableGenRecordGetLoc(self.raw)) }
198    }
199}
200
201macro_rules! try_into {
202    ($type:ty) => {
203        impl<'a> TryFrom<RecordValue<'a>> for $type {
204            type Error = Error;
205
206            fn try_from(record_value: RecordValue<'a>) -> Result<Self, Self::Error> {
207                Self::try_from(record_value.init).map_err(|e| e.set_location(record_value))
208            }
209        }
210    };
211}
212
213try_into!(bool);
214try_into!(Vec<bool>);
215try_into!(Vec<BitInit<'a>>);
216try_into!(i64);
217try_into!(ListInit<'a>);
218try_into!(DagInit<'a>);
219try_into!(Record<'a>);
220try_into!(String);
221try_into!(&'a str);
222
223impl<'a> From<RecordValue<'a>> for TypedInit<'a> {
224    fn from(value: RecordValue<'a>) -> Self {
225        value.init
226    }
227}
228
229/// Struct that represents a field of a [`Record`].
230///
231/// Can be converted into a Rust type using the [`TryInto`] trait.
232#[derive(Debug, Clone, Copy, PartialEq, Eq)]
233pub struct RecordValue<'a> {
234    raw: TableGenRecordValRef,
235    pub name: StringInit<'a>,
236    pub init: TypedInit<'a>,
237    _reference: PhantomData<&'a TableGenRecordRef>,
238}
239
240impl Display for RecordValue<'_> {
241    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
242        let mut data = (formatter, Ok(()));
243
244        unsafe {
245            tableGenRecordValPrint(
246                self.raw,
247                Some(print_callback),
248                &mut data as *mut _ as *mut c_void,
249            );
250        }
251
252        data.1
253    }
254}
255
256impl RecordValue<'_> {
257    /// Creates a record from a raw object.
258    ///
259    /// # Safety
260    ///
261    /// The raw object must be valid.
262    pub unsafe fn from_raw(ptr: TableGenRecordValRef) -> Self {
263        let name = unsafe { StringInit::from_raw(tableGenRecordValGetNameInit(ptr)) };
264        let value = unsafe { TypedInit::from_raw(tableGenRecordValGetValue(ptr)) };
265        Self {
266            name,
267            init: value,
268            raw: ptr,
269            _reference: PhantomData,
270        }
271    }
272}
273
274impl SourceLoc for RecordValue<'_> {
275    fn source_location(self) -> SourceLocation {
276        unsafe { SourceLocation::from_raw(tableGenRecordValGetLoc(self.raw)) }
277    }
278}
279
280#[derive(Debug, Clone)]
281pub struct RecordValueIter<'a> {
282    record: TableGenRecordRef,
283    current: TableGenRecordValRef,
284    _reference: PhantomData<&'a TableGenRecordRef>,
285}
286
287impl<'a> RecordValueIter<'a> {
288    fn new(record: Record<'a>) -> RecordValueIter<'a> {
289        unsafe {
290            RecordValueIter {
291                record: record.raw,
292                current: tableGenRecordGetFirstValue(record.raw),
293                _reference: PhantomData,
294            }
295        }
296    }
297}
298
299impl<'a> Iterator for RecordValueIter<'a> {
300    type Item = RecordValue<'a>;
301
302    fn next(&mut self) -> Option<RecordValue<'a>> {
303        let res = if self.current.is_null() {
304            None
305        } else {
306            unsafe { Some(RecordValue::from_raw(self.current)) }
307        };
308        self.current = unsafe { tableGenRecordValNext(self.record, self.current) };
309        res
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use crate::TableGenParser;
317
318    #[test]
319    fn record() {
320        let rk = TableGenParser::new()
321            .add_source(
322                r#"
323                class A;
324                class B;
325                class C;
326
327                def D1: A;
328                def D2: A, B;
329                def : B, C;
330                "#,
331            )
332            .unwrap()
333            .parse()
334            .expect("valid tablegen");
335        let d2 = rk.def("D2").expect("D2 exists");
336        assert!(d2.subclass_of("A"));
337        assert!(d2.subclass_of("B"));
338        assert!(!d2.subclass_of("C"));
339        assert!(!d2.subclass_of("D"));
340        let anon = rk
341            .defs()
342            .map(|(_name, def)| def)
343            .find(|d| d.anonymous())
344            .expect("anonymous class exists");
345        assert!(!anon.subclass_of("A"));
346        assert!(anon.subclass_of("B"));
347        assert!(anon.subclass_of("C"));
348    }
349
350    #[test]
351    fn single_value() {
352        let rk = TableGenParser::new()
353            .add_source(
354                r#"
355                def A {
356                    int size = 42;
357                }
358                "#,
359            )
360            .unwrap()
361            .parse()
362            .expect("valid tablegen");
363        let a = rk.def("A").expect("def A exists");
364        assert_eq!(a.name(), Ok("A"));
365        assert_eq!(a.int_value("size"), Ok(42));
366        assert_eq!(
367            a.value("size")
368                .and_then(|v| {
369                    assert!(v.name.to_str() == Ok("size"));
370                    v.init.as_int().map_err(|e| e.set_location(v))
371                })
372                .map(|i| i.into()),
373            Ok(42)
374        );
375    }
376
377    #[test]
378    fn values() {
379        let rk = TableGenParser::new()
380            .add_source(
381                r#"
382                def A {
383                    int a = 5;
384                    string n = "hello";
385                }
386                "#,
387            )
388            .unwrap()
389            .parse()
390            .expect("valid tablegen");
391        let a = rk.def("A").expect("def A exists");
392        let values = a.values();
393        assert_eq!(values.clone().count(), 2);
394        for v in values {
395            match v.init {
396                TypedInit::Int(i) => {
397                    assert_eq!(v.name.to_str(), Ok("a"));
398                    assert_eq!(i64::from(i), 5);
399                }
400                TypedInit::String(i) => {
401                    assert_eq!(v.name.to_str(), Ok("n"));
402                    assert_eq!(i.to_str(), Ok("hello"));
403                }
404                _ => panic!("unexpected type"),
405            }
406        }
407    }
408
409    #[test]
410    fn print_error() {
411        let rk = TableGenParser::new()
412            .add_source(
413                r#"
414                class C<int test> {
415                    int a = test;
416                }
417                def A : C<4>;
418                "#,
419            )
420            .unwrap()
421            .parse()
422            .expect("valid tablegen");
423        let a = rk.def("A").expect("def A exists");
424        if let Err(e) = a.string_value("a") {
425            // With source info
426            assert_eq!(
427                format!("{}", e.clone().add_source_info(rk.source_info())).trim(),
428                r#"
429                  error: invalid conversion from Int to alloc::string::String
430                    int a = test;
431                        ^
432                "#
433                .trim()
434            );
435
436            // Without source info
437            drop(rk);
438            assert_eq!(
439                format!("{}", e).trim(),
440                r#"
441                  invalid conversion from Int to alloc::string::String
442                "#
443                .trim()
444            );
445
446            // With incorrect source info
447            let rk = TableGenParser::new()
448                .add_source("def A;")
449                .unwrap()
450                .parse()
451                .expect("valid tablegen");
452            assert_eq!(
453                format!("{}", e.add_source_info(rk.source_info())).trim(),
454                "invalid conversion from Int to alloc::string::String\nfailed to print source information: invalid source location"
455                .trim()
456            );
457        } else {
458            panic!("expected error")
459        }
460    }
461}