#[cfg(feature = "llvm22-0")]
use crate::raw::tableGenBitsInitConvertKnownBitsToInt;
use crate::{
raw::{
TableGenRecTyKind, TableGenTypedInitRef, tableGenBitInitGetValue, tableGenBitInitIsVarBit,
tableGenBitsInitGetBitInit, tableGenBitsInitGetNumBits, tableGenDagRecordArgName,
tableGenDagRecordGet, tableGenDagRecordGetArgNo, tableGenDagRecordNumArgs,
tableGenDagRecordOperator, tableGenDefInitGetValue, tableGenInitDump, tableGenInitPrint,
tableGenInitRecType, tableGenIntInitGetValue, tableGenListInitGetElementType,
tableGenListRecordGet, tableGenListRecordNumElements, tableGenStringInitGetValue,
tableGenVarBitInitGetBitNum, tableGenVarBitInitGetVarName,
},
string_ref::StringRef,
util::print_callback,
};
use paste::paste;
use crate::{
error::{Error, TableGenError},
record::Record,
};
use std::{
ffi::c_void,
fmt::{self, Debug, Display, Formatter},
marker::PhantomData,
str::Utf8Error,
string::FromUtf8Error,
};
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum TypedInit<'a> {
Bit(BitInit<'a>),
Bits(BitsInit<'a>),
Code(StringInit<'a>),
Int(IntInit<'a>),
String(StringInit<'a>),
List(ListInit<'a>),
Dag(DagInit<'a>),
Def(DefInit<'a>),
Invalid,
}
impl TypedInit<'_> {
fn variant_name(&self) -> &'static str {
match self {
TypedInit::Bit(_) => "Bit",
TypedInit::Bits(_) => "Bits",
TypedInit::Code(_) => "Code",
TypedInit::Int(_) => "Int",
TypedInit::String(_) => "String",
TypedInit::List(_) => "List",
TypedInit::Dag(_) => "Dag",
TypedInit::Def(_) => "Def",
TypedInit::Invalid => "Invalid",
}
}
}
impl Display for TypedInit<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Bit(init) => write!(f, "{}", &init),
Self::Bits(init) => write!(f, "{}", &init),
Self::Code(init) => write!(f, "{}", &init),
Self::Int(init) => write!(f, "{}", &init),
Self::String(init) => write!(f, "{}", &init),
Self::List(init) => write!(f, "{}", &init),
Self::Dag(init) => write!(f, "{}", &init),
Self::Def(init) => write!(f, "{}", &init),
Self::Invalid => write!(f, "Invalid"),
}
}
}
impl Debug for TypedInit<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "TypedInit(")?;
let name = self.variant_name();
write!(f, "{name}(")?;
match self {
Self::Bit(init) => write!(f, "{:#?}", &init),
Self::Bits(init) => write!(f, "{:#?}", &init),
Self::Code(init) => write!(f, "{:#?}", &init),
Self::Int(init) => write!(f, "{:#?}", &init),
Self::String(init) => write!(f, "{:#?}", &init),
Self::List(init) => write!(f, "{:#?}", &init),
Self::Dag(init) => write!(f, "{:#?}", &init),
Self::Def(init) => write!(f, "{:#?}", &init),
Self::Invalid => write!(f, ""),
}?;
write!(f, "))")
}
}
impl std::hash::Hash for TypedInit<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
Self::Bit(v) => v.hash(state),
Self::Bits(v) => v.hash(state),
Self::Code(v) => v.hash(state),
Self::Int(v) => v.hash(state),
Self::String(v) => v.hash(state),
Self::List(v) => v.hash(state),
Self::Dag(v) => v.hash(state),
Self::Def(v) => v.hash(state),
Self::Invalid => {}
}
}
}
macro_rules! as_inner {
($name:ident, $variant:ident, $type:ty) => {
paste! {
pub fn [<as_ $name>](self) -> Result<$type<'a>, Error> {
match self {
Self::$variant(v) => Ok(v),
_ => Err(TableGenError::InitConversion {
from: self.variant_name(),
to: std::any::type_name::<$type>()
}.into())
}
}
}
};
}
macro_rules! try_into {
($variant:ident, $init:ty, $type:ty) => {
impl<'a> TryFrom<TypedInit<'a>> for $type {
type Error = Error;
fn try_from(value: TypedInit<'a>) -> Result<Self, Self::Error> {
match value {
TypedInit::$variant(v) => Ok(Self::try_from(v).map_err(TableGenError::from)?),
_ => Err(TableGenError::InitConversion {
from: value.variant_name(),
to: std::any::type_name::<$type>(),
}
.into()),
}
}
}
};
}
try_into!(Bit, BitInit<'a>, bool);
try_into!(Bits, BitsInit<'a>, Vec<BitInit<'a>>);
try_into!(Bits, BitsInit<'a>, Vec<bool>);
try_into!(Int, IntInit<'a>, i64);
try_into!(Def, DefInit<'a>, Record<'a>);
try_into!(List, ListInit<'a>, ListInit<'a>);
try_into!(Dag, DagInit<'a>, DagInit<'a>);
impl<'a> TryFrom<TypedInit<'a>> for String {
type Error = Error;
fn try_from(value: TypedInit<'a>) -> Result<Self, Self::Error> {
match value {
TypedInit::String(v) | TypedInit::Code(v) => {
Ok(Self::try_from(v).map_err(TableGenError::from)?)
}
_ => Err(TableGenError::InitConversion {
from: value.variant_name(),
to: std::any::type_name::<String>(),
}
.into()),
}
}
}
impl<'a> TryFrom<TypedInit<'a>> for &'a str {
type Error = Error;
fn try_from(value: TypedInit<'a>) -> Result<Self, Self::Error> {
match value {
TypedInit::String(v) | TypedInit::Code(v) => {
Ok(v.to_str().map_err(TableGenError::from)?)
}
_ => Err(TableGenError::InitConversion {
from: value.variant_name(),
to: std::any::type_name::<&'a str>(),
}
.into()),
}
}
}
impl<'a> TypedInit<'a> {
as_inner!(bit, Bit, BitInit);
as_inner!(bits, Bits, BitsInit);
as_inner!(code, Code, StringInit);
as_inner!(int, Int, IntInit);
as_inner!(string, String, StringInit);
as_inner!(list, List, ListInit);
as_inner!(dag, Dag, DagInit);
as_inner!(def, Def, DefInit);
#[allow(non_upper_case_globals)]
pub unsafe fn from_raw(init: TableGenTypedInitRef) -> Self {
use TableGenRecTyKind::*;
match unsafe { tableGenInitRecType(init) } {
TableGenBitRecTyKind => Self::Bit(unsafe { BitInit::from_raw(init) }),
TableGenBitsRecTyKind => Self::Bits(unsafe { BitsInit::from_raw(init) }),
TableGenCodeRecTyKind => Self::Code(unsafe { StringInit::from_raw(init) }),
TableGenIntRecTyKind => TypedInit::Int(unsafe { IntInit::from_raw(init) }),
TableGenStringRecTyKind => Self::String(unsafe { StringInit::from_raw(init) }),
TableGenListRecTyKind => TypedInit::List(unsafe { ListInit::from_raw(init) }),
TableGenDagRecTyKind => TypedInit::Dag(unsafe { DagInit::from_raw(init) }),
TableGenRecordRecTyKind => Self::Def(unsafe { DefInit::from_raw(init) }),
_ => Self::Invalid,
}
}
}
macro_rules! init {
($name:ident) => {
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct $name<'a> {
raw: TableGenTypedInitRef,
_reference: PhantomData<&'a TableGenTypedInitRef>,
}
impl<'a> $name<'a> {
pub unsafe fn from_raw(raw: TableGenTypedInitRef) -> Self {
Self {
raw,
_reference: PhantomData,
}
}
pub fn dump(self) {
unsafe { tableGenInitDump(self.raw) }
}
}
impl std::hash::Hash for $name<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.raw.hash(state);
}
}
impl<'a> Display for $name<'a> {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
let mut data = (formatter, Ok(()));
unsafe {
tableGenInitPrint(
self.raw,
Some(print_callback),
&mut data as *mut _ as *mut c_void,
);
}
data.1
}
}
impl<'a> Debug for $name<'a> {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "{}(", stringify!($name))?;
Display::fmt(self, formatter)?;
write!(formatter, ")")
}
}
};
}
init!(BitInit);
impl<'a> BitInit<'a> {
pub fn is_var_bit(self) -> bool {
unsafe { tableGenBitInitIsVarBit(self.raw) != 0 }
}
pub fn as_var_bit(self) -> Option<(&'a str, usize)> {
if !self.is_var_bit() {
return None;
}
let name_ref = unsafe { tableGenVarBitInitGetVarName(self.raw) };
if name_ref.data.is_null() {
return None;
}
let name = unsafe {
std::str::from_utf8(std::slice::from_raw_parts(
name_ref.data as *const u8,
name_ref.len,
))
.ok()?
};
let bit_num = unsafe { tableGenVarBitInitGetBitNum(self.raw) };
Some((name, bit_num))
}
pub fn as_literal(self) -> Option<bool> {
if self.is_var_bit() {
return None;
}
let mut bit = -1i8;
let ok = unsafe { tableGenBitInitGetValue(self.raw, &mut bit) };
if ok > 0 && (bit == 0 || bit == 1) {
Some(bit != 0)
} else {
None
}
}
}
impl<'a> TryFrom<BitInit<'a>> for bool {
type Error = TableGenError;
fn try_from(value: BitInit<'a>) -> Result<Self, Self::Error> {
value.as_literal().ok_or(TableGenError::InitConversion {
from: "VarBitInit",
to: "bool",
})
}
}
init!(BitsInit);
impl<'a> From<BitsInit<'a>> for Vec<BitInit<'a>> {
fn from(value: BitsInit<'a>) -> Self {
(0..value.num_bits())
.map(|i| value.bit(i).expect("index within range"))
.collect()
}
}
impl<'a> TryFrom<BitsInit<'a>> for Vec<bool> {
type Error = TableGenError;
fn try_from(value: BitsInit<'a>) -> Result<Self, Self::Error> {
(0..value.num_bits())
.map(|i| {
value
.bit(i)
.ok_or(TableGenError::InitConversion {
from: "BitsInit",
to: "bool",
})
.and_then(bool::try_from)
})
.collect()
}
}
impl<'a> From<BitsInit<'a>> for Vec<Option<bool>> {
fn from(value: BitsInit<'a>) -> Self {
(0..value.num_bits())
.map(|i| value.bit(i).expect("index within range").as_literal())
.collect()
}
}
impl<'a> BitsInit<'a> {
pub fn bit(self, index: usize) -> Option<BitInit<'a>> {
let bit = unsafe { tableGenBitsInitGetBitInit(self.raw, index) };
if !bit.is_null() {
Some(unsafe { BitInit::from_raw(bit) })
} else {
None
}
}
pub fn num_bits(self) -> usize {
let mut len = 0;
unsafe { tableGenBitsInitGetNumBits(self.raw, &mut len) };
len
}
#[cfg(feature = "llvm22-0")]
pub fn known_bits_to_int(self) -> u64 {
unsafe { tableGenBitsInitConvertKnownBitsToInt(self.raw) }
}
}
init!(IntInit);
impl<'a> TryFrom<IntInit<'a>> for i64 {
type Error = TableGenError;
fn try_from(value: IntInit<'a>) -> Result<Self, Self::Error> {
let mut int: i64 = 0;
let res = unsafe { tableGenIntInitGetValue(value.raw, &mut int) };
if res > 0 {
Ok(int)
} else {
Err(TableGenError::InitConversion {
from: "Int",
to: "i64",
})
}
}
}
init!(StringInit);
impl<'a> TryFrom<StringInit<'a>> for String {
type Error = FromUtf8Error;
fn try_from(value: StringInit<'a>) -> Result<Self, Self::Error> {
String::from_utf8(value.as_bytes().to_vec())
}
}
impl<'a> TryFrom<StringInit<'a>> for &'a str {
type Error = Utf8Error;
fn try_from(value: StringInit<'a>) -> Result<Self, Utf8Error> {
value.to_str()
}
}
impl<'a> StringInit<'a> {
pub fn to_str(self) -> Result<&'a str, Utf8Error> {
unsafe { StringRef::from_raw(tableGenStringInitGetValue(self.raw)) }.try_into()
}
pub fn as_bytes(self) -> &'a [u8] {
unsafe { StringRef::from_raw(tableGenStringInitGetValue(self.raw)) }.into()
}
}
init!(DefInit);
impl<'a> From<DefInit<'a>> for Record<'a> {
fn from(value: DefInit<'a>) -> Self {
unsafe { Record::from_raw(tableGenDefInitGetValue(value.raw)) }
}
}
init!(DagInit);
impl<'a> DagInit<'a> {
pub fn args(self) -> DagIter<'a> {
let back = self.num_args();
DagIter {
dag: self,
index: 0,
back,
}
}
pub fn operator(self) -> Record<'a> {
unsafe { Record::from_raw(tableGenDagRecordOperator(self.raw)) }
}
pub fn num_args(self) -> usize {
unsafe { tableGenDagRecordNumArgs(self.raw) }
}
pub fn name(self, index: usize) -> Option<&'a str> {
unsafe { StringRef::from_option_raw(tableGenDagRecordArgName(self.raw, index)) }
.and_then(|s| s.try_into().ok())
}
pub fn arg_no(self, name: &str) -> Option<usize> {
let result = unsafe { tableGenDagRecordGetArgNo(self.raw, StringRef::from(name).to_raw()) };
if result == usize::MAX {
None
} else {
Some(result)
}
}
pub fn get(self, index: usize) -> Option<TypedInit<'a>> {
let value = unsafe { tableGenDagRecordGet(self.raw, index) };
if !value.is_null() {
Some(unsafe { TypedInit::from_raw(value) })
} else {
None
}
}
}
#[derive(Debug, Clone)]
pub struct DagIter<'a> {
dag: DagInit<'a>,
index: usize,
back: usize,
}
impl<'a> Iterator for DagIter<'a> {
type Item = (Option<&'a str>, TypedInit<'a>);
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.back {
return None;
}
let next = self.dag.get(self.index)?;
let name = self.dag.name(self.index);
self.index += 1;
Some((name, next))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.back.saturating_sub(self.index);
(remaining, Some(remaining))
}
}
impl<'a> DoubleEndedIterator for DagIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.index >= self.back {
return None;
}
self.back -= 1;
match self.dag.get(self.back) {
Some(next) => {
let name = self.dag.name(self.back);
Some((name, next))
}
None => {
self.back += 1;
None
}
}
}
}
impl ExactSizeIterator for DagIter<'_> {}
impl std::iter::FusedIterator for DagIter<'_> {}
init!(ListInit);
impl<'a> ListInit<'a> {
pub fn iter(self) -> ListIter<'a> {
let back = self.len();
ListIter {
list: self,
index: 0,
back,
}
}
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn len(self) -> usize {
unsafe { tableGenListRecordNumElements(self.raw) }
}
pub fn get(self, index: usize) -> Option<TypedInit<'a>> {
let value = unsafe { tableGenListRecordGet(self.raw, index) };
if !value.is_null() {
Some(unsafe { TypedInit::from_raw(value) })
} else {
None
}
}
pub fn element_type(self) -> Option<crate::raw::TableGenRecTyKind::Type> {
use crate::raw::TableGenRecTyKind::TableGenInvalidRecTyKind;
let kind = unsafe { tableGenListInitGetElementType(self.raw) };
if kind == TableGenInvalidRecTyKind {
None
} else {
Some(kind)
}
}
}
#[derive(Debug, Clone)]
pub struct ListIter<'a> {
list: ListInit<'a>,
index: usize,
back: usize,
}
impl<'a> Iterator for ListIter<'a> {
type Item = TypedInit<'a>;
fn next(&mut self) -> Option<TypedInit<'a>> {
if self.index >= self.back {
return None;
}
let next = unsafe { tableGenListRecordGet(self.list.raw, self.index) };
self.index += 1;
if !next.is_null() {
Some(unsafe { TypedInit::from_raw(next) })
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.back.saturating_sub(self.index);
(remaining, Some(remaining))
}
}
impl<'a> DoubleEndedIterator for ListIter<'a> {
fn next_back(&mut self) -> Option<TypedInit<'a>> {
if self.index >= self.back {
return None;
}
self.back -= 1;
let next = unsafe { tableGenListRecordGet(self.list.raw, self.back) };
if !next.is_null() {
Some(unsafe { TypedInit::from_raw(next) })
} else {
self.back += 1;
None
}
}
}
impl ExactSizeIterator for ListIter<'_> {}
impl std::iter::FusedIterator for ListIter<'_> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::TableGenParser;
macro_rules! test_init {
($name:ident, $td_field:expr, $expected:expr) => {
#[test]
fn $name() {
let rk = TableGenParser::new()
.add_source(&format!(
"
def A {{
{}
}}
",
$td_field
))
.unwrap()
.parse()
.expect("valid tablegen");
let a = rk
.def("A")
.expect("def A exists")
.value("a")
.expect("field a exists");
assert_eq!(a.init.try_into(), Ok($expected));
}
};
}
test_init!(bit, "bit a = 0;", false);
test_init!(
bits,
"bits<4> a = { 0, 0, 1, 0 };",
vec![false, true, false, false]
);
test_init!(int, "int a = 42;", 42);
test_init!(string, "string a = \"hi\";", "hi");
test_init!(code, "code a = \"hi\";", "hi");
#[test]
fn dag() {
let rk = TableGenParser::new()
.add_source(
"
def ins;
def X {
int i = 4;
}
def Y {
string s = \"test\";
}
def A {
dag args = (ins X:$src1, Y:$src2);
}
",
)
.unwrap()
.parse()
.expect("valid tablegen");
let a: DagInit = rk
.def("A")
.expect("def A exists")
.value("args")
.expect("field args exists")
.try_into()
.expect("is dag init");
assert_eq!(a.num_args(), 2);
assert_eq!(a.operator().name(), Ok("ins"));
let mut args = a.args();
assert_eq!(
args.clone().next().map(|(name, init)| (
name,
Record::try_from(init).expect("is record").int_value("i")
)),
Some((Some("src1"), Ok(4)))
);
assert_eq!(
args.nth(1).map(|(name, init)| (
name,
Record::try_from(init).expect("is record").string_value("s")
)),
Some((Some("src2"), Ok("test".into())))
);
}
#[test]
fn dag_unnamed_args() {
let rk = TableGenParser::new()
.add_source(
"
def add;
def X { int i = 1; }
def Y { int i = 2; }
def A {
dag args = (add X, Y);
}
",
)
.unwrap()
.parse()
.expect("valid tablegen");
let a: DagInit = rk
.def("A")
.expect("def A exists")
.value("args")
.expect("field args exists")
.try_into()
.expect("is dag init");
assert_eq!(a.num_args(), 2);
let collected: Vec<_> = a
.args()
.map(|(name, init)| {
(
name,
Record::try_from(init).expect("is record").int_value("i"),
)
})
.collect();
assert_eq!(collected, vec![(None, Ok(1)), (None, Ok(2))]);
}
#[test]
fn dag_mixed_named_unnamed_args() {
let rk = TableGenParser::new()
.add_source(
"
def op;
def X { int i = 10; }
def Y { int i = 20; }
def A {
dag args = (op X:$named, Y);
}
",
)
.unwrap()
.parse()
.expect("valid tablegen");
let a: DagInit = rk
.def("A")
.expect("def A exists")
.value("args")
.expect("field args exists")
.try_into()
.expect("is dag init");
assert_eq!(a.num_args(), 2);
let collected: Vec<_> = a
.args()
.map(|(name, init)| {
(
name,
Record::try_from(init).expect("is record").int_value("i"),
)
})
.collect();
assert_eq!(collected, vec![(Some("named"), Ok(10)), (None, Ok(20))]);
}
#[test]
fn list() {
let rk = TableGenParser::new()
.add_source(
"
def A {
list<int> l = [0, 1, 2, 3];
}
",
)
.unwrap()
.parse()
.expect("valid tablegen");
let l: ListInit = rk
.def("A")
.expect("def A exists")
.value("l")
.expect("field args exists")
.try_into()
.expect("is list init");
assert_eq!(l.len(), 4);
let iter = l.iter();
assert_eq!(iter.clone().count(), 4);
assert_eq!(iter.clone().next().unwrap().try_into(), Ok(0));
assert_eq!(iter.clone().nth(1).unwrap().try_into(), Ok(1));
assert_eq!(iter.clone().nth(2).unwrap().try_into(), Ok(2));
assert_eq!(iter.clone().nth(3).unwrap().try_into(), Ok(3));
}
#[test]
fn list_double_ended() {
let rk = TableGenParser::new()
.add_source("def A { list<int> l = [10, 20, 30, 40]; }")
.unwrap()
.parse()
.expect("valid tablegen");
let l: ListInit = rk.def("A").unwrap().value("l").unwrap().try_into().unwrap();
let mut iter = l.iter();
assert_eq!(iter.len(), 4);
assert_eq!(iter.next().unwrap().try_into(), Ok(10i64));
assert_eq!(iter.len(), 3);
assert_eq!(iter.next_back().unwrap().try_into(), Ok(40i64));
assert_eq!(iter.len(), 2);
assert_eq!(iter.next().unwrap().try_into(), Ok(20i64));
assert_eq!(iter.next_back().unwrap().try_into(), Ok(30i64));
assert_eq!(iter.len(), 0);
assert!(iter.next().is_none());
assert!(iter.next_back().is_none());
}
#[test]
fn dag_double_ended() {
let rk = TableGenParser::new()
.add_source(
"def op; def A { int i = 1; } def B { int i = 2; } def C { int i = 3; }
def R { dag d = (op A, B, C); }",
)
.unwrap()
.parse()
.expect("valid tablegen");
let dag: DagInit = rk.def("R").unwrap().value("d").unwrap().try_into().unwrap();
let mut iter = dag.args();
assert_eq!(iter.len(), 3);
let (_, first) = iter.next().unwrap();
assert_eq!(Record::try_from(first).unwrap().int_value("i"), Ok(1));
assert_eq!(iter.len(), 2);
let (_, last) = iter.next_back().unwrap();
assert_eq!(Record::try_from(last).unwrap().int_value("i"), Ok(3));
assert_eq!(iter.len(), 1);
let (_, mid) = iter.next().unwrap();
assert_eq!(Record::try_from(mid).unwrap().int_value("i"), Ok(2));
assert_eq!(iter.len(), 0);
assert!(iter.next().is_none());
assert!(iter.next_back().is_none());
}
#[test]
fn varbit() {
let rk = TableGenParser::new()
.add_source("class Foo<bits<4> src> { bits<4> val = src; }")
.unwrap()
.parse()
.expect("valid tablegen");
let bits: BitsInit = rk
.class("Foo")
.expect("class Foo exists")
.value("val")
.expect("field val exists")
.init
.as_bits()
.expect("is BitsInit");
assert_eq!(bits.num_bits(), 4);
for i in 0..4 {
let bit = bits.bit(i).expect("bit in range");
assert!(bit.is_var_bit());
assert_eq!(bit.as_var_bit(), Some(("Foo:src", i)));
assert_eq!(bit.as_literal(), None);
}
let optional: Vec<Option<bool>> = bits.into();
assert_eq!(optional, vec![None, None, None, None]);
}
#[test]
fn vec_bool_from_varbit_bits_returns_err() {
let rk = TableGenParser::new()
.add_source("class Foo<bits<4> src> { bits<4> val = src; }")
.unwrap()
.parse()
.expect("valid tablegen");
let bits: BitsInit = rk
.class("Foo")
.expect("class Foo exists")
.value("val")
.expect("field val exists")
.init
.as_bits()
.expect("is BitsInit");
let result = Vec::<bool>::try_from(bits);
assert!(result.is_err());
}
#[test]
fn empty_list() {
let rk = TableGenParser::new()
.add_source("def A { list<int> l = []; }")
.unwrap()
.parse()
.expect("valid tablegen");
let l: ListInit = rk
.def("A")
.expect("def A exists")
.value("l")
.expect("field l exists")
.try_into()
.expect("is list init");
assert_eq!(l.len(), 0);
assert!(l.is_empty());
assert!(l.iter().next().is_none());
let mut iter = l.iter();
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
#[test]
fn literal_bit_methods() {
let rk = TableGenParser::new()
.add_source("def A { bits<4> a = { 0, 1, 0, 1 }; }")
.unwrap()
.parse()
.expect("valid tablegen");
let bits: BitsInit = rk
.def("A")
.expect("def A exists")
.value("a")
.expect("field a exists")
.init
.as_bits()
.expect("is BitsInit");
for i in 0..4 {
let bit = bits.bit(i).expect("bit in range");
assert!(!bit.is_var_bit());
assert!(bit.as_var_bit().is_none());
assert!(bit.as_literal().is_some());
}
}
#[test]
fn list_element_type() {
use crate::raw::TableGenRecTyKind::{
TableGenDagRecTyKind, TableGenIntRecTyKind, TableGenStringRecTyKind,
};
let rk = TableGenParser::new()
.add_source(
r#"
def op;
def A {
list<int> li = [1, 2, 3];
list<string> ls = ["a", "b"];
list<dag> ld = [(op)];
}
"#,
)
.unwrap()
.parse()
.expect("valid tablegen");
let a = rk.def("A").expect("def A exists");
let li: ListInit = a.value("li").unwrap().try_into().unwrap();
assert_eq!(li.element_type(), Some(TableGenIntRecTyKind));
let ls: ListInit = a.value("ls").unwrap().try_into().unwrap();
assert_eq!(ls.element_type(), Some(TableGenStringRecTyKind));
let ld: ListInit = a.value("ld").unwrap().try_into().unwrap();
assert_eq!(ld.element_type(), Some(TableGenDagRecTyKind));
}
}