1use rigsql_core::{Token, TokenKind};
2
3pub struct ParseContext<'a> {
5 tokens: &'a [Token],
6 pos: usize,
7 source: &'a str,
8}
9
10impl<'a> ParseContext<'a> {
11 pub fn new(tokens: &'a [Token], source: &'a str) -> Self {
12 Self {
13 tokens,
14 pos: 0,
15 source,
16 }
17 }
18
19 pub fn source(&self) -> &'a str {
20 self.source
21 }
22
23 pub fn pos(&self) -> usize {
25 self.pos
26 }
27
28 pub fn save(&self) -> usize {
30 self.pos
31 }
32
33 pub fn restore(&mut self, pos: usize) {
35 self.pos = pos;
36 }
37
38 pub fn peek(&self) -> Option<&'a Token> {
40 self.tokens.get(self.pos)
41 }
42
43 pub fn peek_kind(&self) -> Option<TokenKind> {
45 self.peek().map(|t| t.kind)
46 }
47
48 pub fn peek_non_trivia(&self) -> Option<&'a Token> {
50 let mut i = self.pos;
51 while i < self.tokens.len() {
52 if !self.tokens[i].kind.is_trivia() {
53 return Some(&self.tokens[i]);
54 }
55 i += 1;
56 }
57 None
58 }
59
60 pub fn peek_keyword(&self, kw: &str) -> bool {
62 self.peek_non_trivia()
63 .is_some_and(|t| t.kind == TokenKind::Word && t.text.eq_ignore_ascii_case(kw))
64 }
65
66 pub fn peek_keywords(&self, kws: &[&str]) -> bool {
68 let mut i = self.pos;
69 for kw in kws {
70 while i < self.tokens.len() && self.tokens[i].kind.is_trivia() {
72 i += 1;
73 }
74 if i >= self.tokens.len() {
75 return false;
76 }
77 let t = &self.tokens[i];
78 if t.kind != TokenKind::Word || !t.text.eq_ignore_ascii_case(kw) {
79 return false;
80 }
81 i += 1;
82 }
83 true
84 }
85
86 pub fn advance(&mut self) -> Option<&'a Token> {
88 if self.pos < self.tokens.len() {
89 let token = &self.tokens[self.pos];
90 self.pos += 1;
91 Some(token)
92 } else {
93 None
94 }
95 }
96
97 pub fn eat_trivia(&mut self) -> Vec<&'a Token> {
99 let mut trivia = Vec::new();
100 while self.pos < self.tokens.len() && self.tokens[self.pos].kind.is_trivia() {
101 trivia.push(&self.tokens[self.pos]);
102 self.pos += 1;
103 }
104 trivia
105 }
106
107 pub fn eat_keyword(&mut self, kw: &str) -> Option<&'a Token> {
109 if self.pos < self.tokens.len()
110 && self.tokens[self.pos].kind == TokenKind::Word
111 && self.tokens[self.pos].text.eq_ignore_ascii_case(kw)
112 {
113 let token = &self.tokens[self.pos];
114 self.pos += 1;
115 Some(token)
116 } else {
117 None
118 }
119 }
120
121 pub fn eat_kind(&mut self, kind: TokenKind) -> Option<&'a Token> {
123 if self.pos < self.tokens.len() && self.tokens[self.pos].kind == kind {
124 let token = &self.tokens[self.pos];
125 self.pos += 1;
126 Some(token)
127 } else {
128 None
129 }
130 }
131
132 pub fn at_eof(&self) -> bool {
134 self.pos >= self.tokens.len() || self.tokens[self.pos].kind == TokenKind::Eof
135 }
136
137 pub fn remaining(&self) -> &'a [Token] {
139 &self.tokens[self.pos..]
140 }
141}