use rowan::TextRange;
use crate::hir::FileId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum NodeClass {
Stmt = 0,
Expr = 1,
Tag = 16,
Knot = 17,
Stitch = 18,
LogicBlock = 19,
Break = 20,
Continue = 21,
If = 22,
While = 23,
Await = 24,
For = 25,
Choice = 26,
Content = 27,
Conditional = 28,
Sequence = 29,
Divert = 30,
TunnelCall = 31,
ThreadStart = 32,
Return = 33,
FnLiteral = 34,
RefArg = 35,
StructLiteral = 36,
FieldAccess = 37,
ArrayLiteral = 38,
MapLiteral = 39,
Index = 40,
Range = 41,
VarDecl = 42,
ConstDecl = 43,
TempDecl = 44,
Assignment = 45,
ListDecl = 46,
StructDecl = 47,
ExternalDecl = 48,
Include = 49,
Infix = 50,
ConditionalBranch = 51,
SequenceBranch = 52,
Lambda = 53,
Span = 54,
SpanAttr = 55,
}
impl NodeClass {
#[must_use]
pub const fn as_u16(self) -> u16 {
self as u16
}
#[must_use]
pub const fn from_u16(value: u16) -> Option<Self> {
Some(match value {
0 => Self::Stmt,
1 => Self::Expr,
16 => Self::Tag,
17 => Self::Knot,
18 => Self::Stitch,
19 => Self::LogicBlock,
20 => Self::Break,
21 => Self::Continue,
22 => Self::If,
23 => Self::While,
24 => Self::Await,
25 => Self::For,
26 => Self::Choice,
27 => Self::Content,
28 => Self::Conditional,
29 => Self::Sequence,
30 => Self::Divert,
31 => Self::TunnelCall,
32 => Self::ThreadStart,
33 => Self::Return,
34 => Self::FnLiteral,
35 => Self::RefArg,
36 => Self::StructLiteral,
37 => Self::FieldAccess,
38 => Self::ArrayLiteral,
39 => Self::MapLiteral,
40 => Self::Index,
41 => Self::Range,
42 => Self::VarDecl,
43 => Self::ConstDecl,
44 => Self::TempDecl,
45 => Self::Assignment,
46 => Self::ListDecl,
47 => Self::StructDecl,
48 => Self::ExternalDecl,
49 => Self::Include,
50 => Self::Infix,
51 => Self::ConditionalBranch,
52 => Self::SequenceBranch,
53 => Self::Lambda,
54 => Self::Span,
55 => Self::SpanAttr,
_ => return None,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KindToken {
pub class: NodeClass,
pub raw: u16,
}
impl KindToken {
pub const SYNTHETIC_RAW: u16 = u16::MAX;
#[must_use]
pub const fn synthetic(class: NodeClass) -> Self {
Self {
class,
raw: Self::SYNTHETIC_RAW,
}
}
#[must_use]
pub const fn as_u32(self) -> u32 {
((self.class.as_u16() as u32) << 16) | self.raw as u32
}
#[must_use]
pub fn from_u32(value: u32) -> Option<Self> {
let Ok(class) = u16::try_from(value >> 16) else {
return None;
};
let Ok(raw) = u16::try_from(value & 0xFFFF) else {
return None;
};
NodeClass::from_u16(class).map(|class| Self { class, raw })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Provenance {
pub file: FileId,
pub range: TextRange,
pub kind: KindToken,
}
impl Provenance {
#[must_use]
pub const fn new(file: FileId, range: TextRange, kind: KindToken) -> Self {
Self { file, range, kind }
}
#[must_use]
pub const fn text_range(&self) -> TextRange {
self.range
}
#[must_use]
pub const fn class(&self) -> NodeClass {
self.kind.class
}
#[must_use]
pub fn synthetic(class: NodeClass, range: TextRange) -> Self {
Self {
file: FileId(u32::MAX),
range,
kind: KindToken::synthetic(class),
}
}
}
pub trait ProvenanceResolver {
type Node;
fn resolve(&self, provenance: Provenance) -> Option<Self::Node>;
}
#[cfg(test)]
mod tests {
use super::*;
use rowan::{TextRange, TextSize};
#[test]
fn node_class_u16_round_trips() {
for v in 0..=u16::MAX {
if let Some(class) = NodeClass::from_u16(v) {
assert_eq!(class.as_u16(), v);
}
}
assert_eq!(NodeClass::from_u16(NodeClass::SpanAttr.as_u16() + 1), None);
assert_eq!(NodeClass::from_u16(2), None, "generic range is reserved");
}
#[test]
fn kind_token_u32_round_trips() {
let token = KindToken {
class: NodeClass::Stitch,
raw: 137,
};
assert_eq!(KindToken::from_u32(token.as_u32()), Some(token));
assert_eq!(KindToken::from_u32(0xFFFF_0000), None);
}
#[test]
fn provenance_is_plain_reconstructible_data() {
let range = TextRange::new(TextSize::new(3), TextSize::new(9));
let original = Provenance::new(
FileId(7),
range,
KindToken {
class: NodeClass::Knot,
raw: 42,
},
);
let rebuilt = Provenance::new(
FileId(original.file.0),
original.range,
KindToken::from_u32(original.kind.as_u32()).unwrap(),
);
assert_eq!(original, rebuilt);
}
}