1use rigsql_core::{Token, TokenKind};
2
3#[derive(Debug, Clone)]
5pub struct ParseDiagnostic {
6 pub offset: u32,
8 pub message: String,
10}
11
12pub struct ParseContext<'a> {
14 tokens: &'a [Token],
15 pos: usize,
16 source: &'a str,
17 diagnostics: Vec<ParseDiagnostic>,
19}
20
21impl<'a> ParseContext<'a> {
22 pub fn new(tokens: &'a [Token], source: &'a str) -> Self {
23 Self {
24 tokens,
25 pos: 0,
26 source,
27 diagnostics: Vec::new(),
28 }
29 }
30
31 pub fn source(&self) -> &'a str {
32 self.source
33 }
34
35 pub fn record_error(&mut self, message: &str) {
37 let offset = self
38 .peek()
39 .map(|t| t.span.start)
40 .unwrap_or_else(|| self.source.len() as u32);
41 self.diagnostics.push(ParseDiagnostic {
42 offset,
43 message: message.to_string(),
44 });
45 }
46
47 pub fn record_error_at(&mut self, offset: u32, message: &str) {
49 self.diagnostics.push(ParseDiagnostic {
50 offset,
51 message: message.to_string(),
52 });
53 }
54
55 pub fn take_diagnostics(&mut self) -> Vec<ParseDiagnostic> {
57 std::mem::take(&mut self.diagnostics)
58 }
59
60 pub fn pos(&self) -> usize {
62 self.pos
63 }
64
65 pub fn save(&self) -> usize {
67 self.pos
68 }
69
70 pub fn restore(&mut self, pos: usize) {
72 self.pos = pos;
73 }
74
75 pub fn peek(&self) -> Option<&'a Token> {
77 self.tokens.get(self.pos)
78 }
79
80 pub fn peek_kind(&self) -> Option<TokenKind> {
82 self.peek().map(|t| t.kind)
83 }
84
85 pub fn peek_non_trivia(&self) -> Option<&'a Token> {
87 let mut i = self.pos;
88 while i < self.tokens.len() {
89 if !self.tokens[i].kind.is_trivia() {
90 return Some(&self.tokens[i]);
91 }
92 i += 1;
93 }
94 None
95 }
96
97 pub fn peek_keyword(&self, kw: &str) -> bool {
99 self.peek_non_trivia()
100 .is_some_and(|t| t.kind == TokenKind::Word && t.text.eq_ignore_ascii_case(kw))
101 }
102
103 pub fn peek_keywords(&self, kws: &[&str]) -> bool {
105 let mut i = self.pos;
106 for kw in kws {
107 while i < self.tokens.len() && self.tokens[i].kind.is_trivia() {
109 i += 1;
110 }
111 if i >= self.tokens.len() {
112 return false;
113 }
114 let t = &self.tokens[i];
115 if t.kind != TokenKind::Word || !t.text.eq_ignore_ascii_case(kw) {
116 return false;
117 }
118 i += 1;
119 }
120 true
121 }
122
123 pub fn advance(&mut self) -> Option<&'a Token> {
125 if self.pos < self.tokens.len() {
126 let token = &self.tokens[self.pos];
127 self.pos += 1;
128 Some(token)
129 } else {
130 None
131 }
132 }
133
134 pub fn eat_trivia(&mut self) -> Vec<&'a Token> {
136 let mut trivia = Vec::new();
137 while self.pos < self.tokens.len() && self.tokens[self.pos].kind.is_trivia() {
138 trivia.push(&self.tokens[self.pos]);
139 self.pos += 1;
140 }
141 trivia
142 }
143
144 pub fn eat_keyword(&mut self, kw: &str) -> Option<&'a Token> {
146 if self.pos < self.tokens.len()
147 && self.tokens[self.pos].kind == TokenKind::Word
148 && self.tokens[self.pos].text.eq_ignore_ascii_case(kw)
149 {
150 let token = &self.tokens[self.pos];
151 self.pos += 1;
152 Some(token)
153 } else {
154 None
155 }
156 }
157
158 pub fn eat_kind(&mut self, kind: TokenKind) -> Option<&'a Token> {
160 if self.pos < self.tokens.len() && self.tokens[self.pos].kind == kind {
161 let token = &self.tokens[self.pos];
162 self.pos += 1;
163 Some(token)
164 } else {
165 None
166 }
167 }
168
169 pub fn at_eof(&self) -> bool {
171 self.pos >= self.tokens.len() || self.tokens[self.pos].kind == TokenKind::Eof
172 }
173
174 pub fn remaining(&self) -> &'a [Token] {
176 &self.tokens[self.pos..]
177 }
178}