1use std::rc::Rc;
16
17use crate::cmd::Cmd;
18use crate::hash::CsId;
19
20pub const CS_TOKEN_FLAG: u32 = 0x2000_0000;
22const CMD_SHIFT: u32 = 21;
24const CHR_MASK: u32 = (1 << CMD_SHIFT) - 1;
25
26#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, Default)]
28pub struct Token(pub u32);
29
30impl Token {
31 #[inline]
33 pub const fn char(cmd: Cmd, chr: u32) -> Token {
34 debug_assert!(((cmd as u32) << CMD_SHIFT) < CS_TOKEN_FLAG);
35 Token(((cmd as u32) << CMD_SHIFT) | chr)
36 }
37
38 #[inline]
40 pub const fn cs(cs: CsId) -> Token {
41 Token(CS_TOKEN_FLAG + cs.0)
42 }
43
44 #[inline]
46 pub const fn is_cs(self) -> bool {
47 self.0 >= CS_TOKEN_FLAG
48 }
49
50 #[inline]
52 pub const fn cs_id(self) -> CsId {
53 CsId(self.0 - CS_TOKEN_FLAG)
54 }
55
56 #[inline]
58 pub fn cmd(self) -> Cmd {
59 debug_assert!(!self.is_cs());
60 Cmd::ALL[(self.0 >> CMD_SHIFT) as usize]
61 }
62
63 #[inline]
65 pub const fn cmd_code(self) -> u32 {
66 self.0 >> CMD_SHIFT
67 }
68
69 #[inline]
71 pub const fn chr(self) -> u32 {
72 self.0 & CHR_MASK
73 }
74
75 pub const fn match_token(chr: u32) -> Token {
78 Token::char(Cmd::ParEnd, chr)
79 }
80
81 pub const END_MATCH: Token = Token::char(Cmd::Stop, 0);
83
84 pub const fn out_param(n: u32) -> Token {
86 Token::char(Cmd::CarRet, n)
87 }
88
89 pub const LEFT_BRACE: Token = Token::char(Cmd::LeftBrace, '{' as u32);
91 pub const RIGHT_BRACE: Token = Token::char(Cmd::RightBrace, '}' as u32);
93 pub const SPACE: Token = Token::char(Cmd::Spacer, ' ' as u32);
95 pub const fn other(c: u32) -> Token {
97 Token::char(Cmd::OtherChar, c)
98 }
99 pub const fn letter(c: u32) -> Token {
101 Token::char(Cmd::Letter, c)
102 }
103}
104
105pub type TokenList = Rc<[Token]>;
108
109pub fn empty_list() -> TokenList {
111 thread_local! {
112 static EMPTY: TokenList = Rc::from(Vec::new());
113 }
114 EMPTY.with(Rc::clone)
115}
116
117#[derive(Debug, Clone)]
119pub struct Macro {
120 pub protected: bool,
122 pub tokens: TokenList,
124 pub native: std::cell::Cell<u16>,
128 pub origin: std::cell::Cell<u32>,
132}
133
134impl Macro {
135 pub fn new(protected: bool, tokens: TokenList) -> Macro {
137 Macro {
138 protected,
139 tokens,
140 native: std::cell::Cell::new(0),
141 origin: std::cell::Cell::new(0),
142 }
143 }
144}
145
146impl PartialEq for Macro {
148 fn eq(&self, other: &Macro) -> bool {
149 self.protected == other.protected && self.tokens == other.tokens
150 }
151}
152
153impl Eq for Macro {}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn macro_origin_fits_in_padding() {
161 assert_eq!(std::mem::size_of::<Macro>(), 24);
162 }
163
164 #[test]
165 fn packing() {
166 let t = Token::char(Cmd::Letter, 0x10FFFF);
167 assert!(!t.is_cs());
168 assert_eq!(t.cmd(), Cmd::Letter);
169 assert_eq!(t.chr(), 0x10FFFF);
170 let c = Token::cs(CsId(12345));
171 assert!(c.is_cs());
172 assert_eq!(c.cs_id(), CsId(12345));
173 assert!(Token::char(Cmd::DelimNum, 0x1F_FFFF).0 < CS_TOKEN_FLAG);
174 }
175}