doge_compiler/parser/
mod.rs1pub(super) use crate::ast::{BinOp, Expr, InterpPart, Param, Params, Script, Stmt, UnOp};
2pub(super) use crate::diagnostics::Diagnostic;
3use crate::lexer;
4pub(super) use crate::token::{Span, StrSegment, Token, TokenKind};
5
6mod expr;
7mod stmt;
8#[cfg(test)]
9mod tests;
10
11pub fn parse(path: &str, source: &str) -> Result<Script, Diagnostic> {
12 let tokens = lexer::lex(path, source)?;
13 Parser::new(path, source, tokens).parse_script()
14}
15
16pub enum ReplParse {
18 Complete(Script),
20 Incomplete(Diagnostic),
24 Error(Diagnostic),
26}
27
28pub fn parse_repl(path: &str, source: &str) -> ReplParse {
35 let tokens = match lexer::lex(path, source) {
36 Ok(tokens) => tokens,
37 Err(diag) => {
38 return if incomplete_lex(&diag) {
39 ReplParse::Incomplete(diag)
40 } else {
41 ReplParse::Error(diag)
42 };
43 }
44 };
45 Parser::new(path, source, tokens).parse_repl_script()
46}
47
48fn incomplete_lex(diag: &Diagnostic) -> bool {
52 matches!(
53 diag.headline.as_str(),
54 "very open. much bracket." | "very string. much unfinished." | "very hole. much open."
55 )
56}
57
58struct Parser {
59 path: String,
60 lines: Vec<String>,
61 tokens: Vec<Token>,
62 pos: usize,
63}
64
65impl Parser {
66 fn new(path: &str, source: &str, tokens: Vec<Token>) -> Parser {
67 let lines = crate::diagnostics::split_source_lines(source);
68 Parser {
69 path: path.to_string(),
70 lines,
71 tokens,
72 pos: 0,
73 }
74 }
75
76 fn sub(&self, tokens: Vec<Token>) -> Parser {
80 Parser {
81 path: self.path.clone(),
82 lines: self.lines.clone(),
83 tokens,
84 pos: 0,
85 }
86 }
87
88 fn peek(&self) -> &TokenKind {
91 &self.tokens[self.pos].kind
93 }
94
95 fn peek_next(&self) -> &TokenKind {
98 let next = (self.pos + 1).min(self.tokens.len() - 1);
99 &self.tokens[next].kind
100 }
101
102 fn current_span(&self) -> Span {
103 self.tokens[self.pos].span
104 }
105
106 fn advance(&mut self) -> Token {
107 let token = self.tokens[self.pos].clone();
108 if self.pos + 1 < self.tokens.len() {
109 self.pos += 1;
110 }
111 token
112 }
113
114 fn is(&self, kind: &TokenKind) -> bool {
115 self.peek() == kind
116 }
117
118 fn eat(&mut self, kind: TokenKind) -> Result<Token, Diagnostic> {
121 if self.peek() == &kind {
122 Ok(self.advance())
123 } else {
124 let span = self.current_span();
125 Err(self.diag(
126 span,
127 format!(
128 "doge expected {} here, but found {}",
129 kind.describe(),
130 self.peek().describe()
131 ),
132 ))
133 }
134 }
135
136 fn eat_ident(&mut self, what: &str) -> Result<(String, Span), Diagnostic> {
138 let span = self.current_span();
139 if let TokenKind::Ident(name) = self.peek() {
140 let name = name.clone();
141 self.advance();
142 Ok((name, span))
143 } else {
144 Err(self.diag(
145 span,
146 format!(
147 "doge expected {what} here, but found {}",
148 self.peek().describe()
149 ),
150 ))
151 }
152 }
153
154 fn diag(&self, span: Span, message: impl Into<String>) -> Diagnostic {
155 let source_line = crate::diagnostics::source_line(&self.lines, span.line);
156 Diagnostic::new(&self.path, span.line, span.col, source_line, message)
157 }
158
159 fn missing_wow(&self, span: Span, what: &str) -> Diagnostic {
160 self.diag(span, format!("expected wow to close this {what}"))
161 .with_headline("very incomplete. such missing wow.")
162 .with_hint(
163 "every function, object, and script ends with wow (did the script end early?)",
164 )
165 }
166
167 fn parse_script(&mut self) -> Result<Script, Diagnostic> {
170 let mut stmts = Vec::new();
171 while !self.is(&TokenKind::Wow) {
172 if self.is(&TokenKind::Eof) {
173 return Err(self.missing_wow(self.current_span(), "script"));
174 }
175 stmts.push(self.parse_statement()?);
176 }
177 self.eat(TokenKind::Wow)?;
178 if self.is(&TokenKind::Newline) {
180 self.advance();
181 }
182 if !self.is(&TokenKind::Eof) {
183 let span = self.current_span();
184 return Err(self
185 .diag(
186 span,
187 "doge stops reading at wow — nothing may come after it",
188 )
189 .with_headline("very extra. much after wow.")
190 .with_hint("remove the lines after wow, or move them above it"));
191 }
192 Ok(Script { stmts })
193 }
194
195 fn parse_repl_script(&mut self) -> ReplParse {
203 let mut stmts = Vec::new();
204 while !self.is(&TokenKind::Eof) && !self.is(&TokenKind::Wow) {
205 match self.parse_statement() {
206 Ok(stmt) => stmts.push(stmt),
207 Err(diag) => {
208 return if self.is(&TokenKind::Eof) {
209 ReplParse::Incomplete(diag)
210 } else {
211 ReplParse::Error(diag)
212 };
213 }
214 }
215 }
216 ReplParse::Complete(Script { stmts })
217 }
218
219 fn python_habit(&self, message: &str, hint: &str) -> Diagnostic {
220 let span = self.current_span();
221 self.diag(span, message)
222 .with_headline("very python. much habit.")
223 .with_hint(hint)
224 }
225}