Skip to main content

libperl_macrogen/
token_source.rs

1//! トークンソースの抽象化
2//!
3//! Parser がプリプロセッサ以外のソース(トークン列など)からも
4//! トークンを読めるようにするための抽象化層。
5
6use crate::error::Result;
7use crate::intern::StringInterner;
8use crate::source::{FileRegistry, SourceLocation};
9use crate::token::{Token, TokenKind};
10
11/// トークンを供給するソースの抽象化
12pub trait TokenSource {
13    /// 次のトークンを取得
14    fn next_token(&mut self) -> Result<Token>;
15
16    /// トークンを先読みバッファに戻す
17    fn unget_token(&mut self, token: Token);
18
19    /// StringInterner への参照を取得
20    fn interner(&self) -> &StringInterner;
21
22    /// StringInterner への可変参照を取得
23    fn interner_mut(&mut self) -> &mut StringInterner;
24
25    /// FileRegistry への参照を取得
26    fn files(&self) -> &FileRegistry;
27
28    /// 指定されたファイルがターゲットディレクトリ内かどうかを判定
29    /// デフォルト実装は常に false を返す(TokenSlice など)
30    fn is_file_in_target(&self, file_id: crate::source::FileId) -> bool {
31        let _ = file_id;
32        false
33    }
34}
35
36/// トークン列からトークンを供給する実装
37///
38/// マクロ本体などの既存トークン列をパースする際に使用
39pub struct TokenSlice {
40    tokens: Vec<Token>,
41    pos: usize,
42    interner: StringInterner,
43    files: FileRegistry,
44    eof_loc: SourceLocation,
45    /// 先読みバッファ(unget_token で戻されたトークン)
46    lookahead: Vec<Token>,
47}
48
49impl TokenSlice {
50    /// 新しい TokenSlice を作成
51    ///
52    /// # Arguments
53    /// * `tokens` - パースするトークン列
54    /// * `interner` - 文字列インターナー(既存のものをクローン)
55    /// * `files` - ファイルレジストリ(既存のものをクローン)
56    pub fn new(tokens: Vec<Token>, interner: StringInterner, files: FileRegistry) -> Self {
57        // EOF用の位置情報を設定
58        let eof_loc = tokens.last()
59            .map(|t| t.loc.clone())
60            .unwrap_or_default();
61
62        Self {
63            tokens,
64            pos: 0,
65            interner,
66            files,
67            eof_loc,
68            lookahead: Vec::new(),
69        }
70    }
71
72    /// 現在位置を取得
73    pub fn position(&self) -> usize {
74        self.pos
75    }
76
77    /// 残りトークン数を取得
78    pub fn remaining(&self) -> usize {
79        self.tokens.len().saturating_sub(self.pos)
80    }
81}
82
83impl TokenSource for TokenSlice {
84    fn next_token(&mut self) -> Result<Token> {
85        // 先読みバッファがあればそこから取得
86        if let Some(token) = self.lookahead.pop() {
87            return Ok(token);
88        }
89        if self.pos < self.tokens.len() {
90            let token = self.tokens[self.pos].clone();
91            self.pos += 1;
92            Ok(token)
93        } else {
94            // EOF トークンを返す
95            Ok(Token::new(TokenKind::Eof, self.eof_loc.clone()))
96        }
97    }
98
99    fn unget_token(&mut self, token: Token) {
100        self.lookahead.push(token);
101    }
102
103    fn interner(&self) -> &StringInterner {
104        &self.interner
105    }
106
107    fn interner_mut(&mut self) -> &mut StringInterner {
108        &mut self.interner
109    }
110
111    fn files(&self) -> &FileRegistry {
112        &self.files
113    }
114}
115
116/// 参照ベースのトークンスライス(クローンなし)
117///
118/// マクロ本体のパース時など、既存の interner/files を借用して使う場合に使用。
119/// これにより高コストなクローン操作を回避できる。
120pub struct TokenSliceRef<'a> {
121    tokens: Vec<Token>,
122    pos: usize,
123    interner: &'a StringInterner,
124    files: &'a FileRegistry,
125    eof_loc: SourceLocation,
126    /// 先読みバッファ(unget_token で戻されたトークン)
127    lookahead: Vec<Token>,
128}
129
130impl<'a> TokenSliceRef<'a> {
131    /// 新しい TokenSliceRef を作成
132    pub fn new(
133        tokens: Vec<Token>,
134        interner: &'a StringInterner,
135        files: &'a FileRegistry,
136    ) -> Self {
137        let eof_loc = tokens.last()
138            .map(|t| t.loc.clone())
139            .unwrap_or_default();
140
141        Self {
142            tokens,
143            pos: 0,
144            interner,
145            files,
146            eof_loc,
147            lookahead: Vec::new(),
148        }
149    }
150
151    /// 次のトークンを取得
152    pub fn next_token(&mut self) -> Result<Token> {
153        if let Some(token) = self.lookahead.pop() {
154            return Ok(token);
155        }
156        if self.pos < self.tokens.len() {
157            let token = self.tokens[self.pos].clone();
158            self.pos += 1;
159            Ok(token)
160        } else {
161            Ok(Token::new(TokenKind::Eof, self.eof_loc.clone()))
162        }
163    }
164
165    /// StringInterner への参照を取得
166    pub fn interner(&self) -> &StringInterner {
167        self.interner
168    }
169
170    /// FileRegistry への参照を取得
171    pub fn files(&self) -> &FileRegistry {
172        self.files
173    }
174}
175
176impl<'a> TokenSource for TokenSliceRef<'a> {
177    fn next_token(&mut self) -> Result<Token> {
178        if let Some(token) = self.lookahead.pop() {
179            return Ok(token);
180        }
181        if self.pos < self.tokens.len() {
182            let token = self.tokens[self.pos].clone();
183            self.pos += 1;
184            Ok(token)
185        } else {
186            Ok(Token::new(TokenKind::Eof, self.eof_loc.clone()))
187        }
188    }
189
190    fn unget_token(&mut self, token: Token) {
191        self.lookahead.push(token);
192    }
193
194    fn interner(&self) -> &StringInterner {
195        self.interner
196    }
197
198    fn interner_mut(&mut self) -> &mut StringInterner {
199        // TokenSliceRef は読み取り専用なので interner_mut() は呼ばれない
200        // from_source_with_typedefs() 経由で使用すること
201        panic!("interner_mut() called on TokenSliceRef - use from_source_with_typedefs()")
202    }
203
204    fn files(&self) -> &FileRegistry {
205        self.files
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212    use std::path::PathBuf;
213
214    #[test]
215    fn test_token_slice_empty() {
216        let interner = StringInterner::new();
217        let mut files = FileRegistry::new();
218        files.register(PathBuf::from("test.c"));
219
220        let mut slice = TokenSlice::new(vec![], interner, files);
221
222        let token = slice.next_token().unwrap();
223        assert!(matches!(token.kind, TokenKind::Eof));
224    }
225
226    #[test]
227    fn test_token_slice_tokens() {
228        let mut interner = StringInterner::new();
229        let mut files = FileRegistry::new();
230        let file_id = files.register(PathBuf::from("test.c"));
231        let loc = SourceLocation::new(file_id, 1, 1);
232
233        let foo = interner.intern("foo");
234        let tokens = vec![
235            Token::new(TokenKind::Ident(foo), loc.clone()),
236            Token::new(TokenKind::Plus, loc.clone()),
237            Token::new(TokenKind::IntLit(42), loc.clone()),
238        ];
239
240        let mut slice = TokenSlice::new(tokens, interner, files);
241
242        assert_eq!(slice.remaining(), 3);
243
244        let t1 = slice.next_token().unwrap();
245        assert!(matches!(t1.kind, TokenKind::Ident(_)));
246
247        let t2 = slice.next_token().unwrap();
248        assert!(matches!(t2.kind, TokenKind::Plus));
249
250        let t3 = slice.next_token().unwrap();
251        assert!(matches!(t3.kind, TokenKind::IntLit(42)));
252
253        let t4 = slice.next_token().unwrap();
254        assert!(matches!(t4.kind, TokenKind::Eof));
255
256        assert_eq!(slice.remaining(), 0);
257    }
258}