1use crate::lexer::lex::lex;
2use crate::lexer::token::Token;
3use crate::lexer::token_type::TokenType;
4use ocelot_ast::expression::Expression;
5use ocelot_ast::expression_kind::ExpressionKind;
6use ocelot_ast::identifier_expression::IdentifierExpression;
7use ocelot_ast::item::Item;
8use ocelot_ast::item_kind::ItemKind;
9use ocelot_ast::println_statement::PrintlnStatement;
10use ocelot_ast::script::Script;
11use ocelot_ast::statement::Statement;
12use ocelot_ast::statement_kind::StatementKind;
13use ocelot_ast::string_literal_expression::StringLiteralExpression;
14use ocelot_ast::test_item::TestItem;
15use ocelot_base::compilation_context::CompilationContext;
16use ocelot_base::compilation_stage::CompilationStage;
17use ocelot_base::diagnostic_level::DiagnosticLevel;
18use ocelot_base::error::ErrorKind;
19use ocelot_base::error::OcelotError;
20use ocelot_base::result::OcelotResult;
21use ocelot_base::shared_string::SharedString;
22use ocelot_base::source_annotation::SourceAnnotation;
23use ocelot_base::source_diagnostic::SourceDiagnostic;
24use ocelot_base::source_excerpt::SourceExcerpt;
25use ocelot_base::source_file::SourceFile;
26use ocelot_base::span::Span;
27
28pub struct Parser<'a> {
30 source_file: &'a SourceFile,
31 compilation_context: &'a mut CompilationContext,
32 tokens: Vec<Token>,
33 position: usize,
34}
35
36impl<'a> Parser<'a> {
37 pub fn new(
39 source_file: &'a SourceFile,
40 compilation_context: &'a mut CompilationContext,
41 ) -> Self {
42 let tokens = lex(source_file, compilation_context);
43 Self {
44 source_file,
45 compilation_context,
46 tokens,
47 position: 0,
48 }
49 }
50
51 pub fn parse_script(&mut self) -> OcelotResult<Option<Script>> {
53 if self.compilation_context.has_errors() {
54 return Ok(None);
55 }
56
57 let mut items = Vec::new();
58
59 while !self.at(TokenType::EndOfFile) {
60 match self.parse_item() {
61 Ok(item) => items.push(item),
62 Err(error) if is_parser_compilation_error(&error) => return Ok(None),
63 Err(error) => return Err(error),
64 }
65 }
66
67 Ok(Some(Script::new(
68 items,
69 Span::new(0, self.source_file.source().len()),
70 )))
71 }
72
73 fn parse_item(&mut self) -> OcelotResult<Item> {
74 match self.current().token_type {
75 TokenType::Test => self.parse_test_item(),
76 _ => Ok({
77 let statement = self.parse_statement()?;
78 let span = statement.span.clone();
79 Item::new(ItemKind::Statement(statement), span)
80 }),
81 }
82 }
83
84 fn parse_test_item(&mut self) -> OcelotResult<Item> {
85 let test_token = self.expect(TokenType::Test, "expected `test` item")?;
86 let name_token = self.expect(TokenType::String, "expected test name string")?;
87 let name_literal = self.source_text(&name_token.span);
88 let name = SharedString::from(&name_literal[1..name_literal.len() - 1]);
89 self.expect(TokenType::LeftBrace, "expected `{` after test name")?;
90
91 let mut body = Vec::new();
92 while !self.at(TokenType::RightBrace) {
93 if self.at(TokenType::EndOfFile) {
94 return self.emit_fatal_diagnostic(
95 "expected `}` to close test body",
96 self.current().span.clone(),
97 "test body ends here",
98 );
99 }
100 body.push(self.parse_statement()?);
101 }
102
103 let right_brace = self.expect(TokenType::RightBrace, "expected `}` after test body")?;
104 let span = Span::new(test_token.span.start(), right_brace.span.end());
105 Ok(Item::new(
106 ItemKind::Test(TestItem::new(name, body, span.clone())),
107 span,
108 ))
109 }
110
111 fn parse_statement(&mut self) -> OcelotResult<Statement> {
112 let start = self.current().span.start();
113 let identifier = self.expect(TokenType::Identifier, "expected statement")?;
114 let name = self.source_text(&identifier.span);
115
116 if name != "println" {
117 return self.emit_fatal_diagnostic(
118 "expected `println` statement",
119 identifier.span,
120 "statement is not supported",
121 );
122 }
123
124 self.expect(TokenType::LeftParen, "expected `(` after `println`")?;
125 if self.at(TokenType::RightParen) {
126 return self.emit_fatal_diagnostic(
127 "type error: `println` expects exactly one argument",
128 self.current().span.clone(),
129 "missing argument",
130 );
131 }
132 let argument = self.parse_expression()?;
133 self.expect(TokenType::RightParen, "expected `)` after argument")?;
134 let semicolon = self.expect(TokenType::Semicolon, "expected `;` after statement")?;
135 let statement_span = Span::new(start, semicolon.span.end());
136
137 Ok(Statement::new(
138 StatementKind::Println(PrintlnStatement::new(argument)),
139 statement_span,
140 ))
141 }
142
143 fn parse_expression(&mut self) -> OcelotResult<Expression> {
144 let token = self.current().clone();
145
146 match token.token_type {
147 TokenType::String => {
148 self.position += 1;
149 let literal = self.source_text(&token.span);
150 let value = literal[1..literal.len() - 1].to_owned();
151 Ok(Expression::new(
152 ExpressionKind::StringLiteral(StringLiteralExpression::new(value)),
153 token.span,
154 ))
155 }
156 TokenType::Identifier => {
157 self.position += 1;
158 Ok(Expression::new(
159 ExpressionKind::Identifier(IdentifierExpression::new(
160 self.source_text(&token.span),
161 )),
162 token.span,
163 ))
164 }
165 TokenType::Unexpected => {
166 self.emit_fatal_diagnostic("unexpected token", token.span, "unexpected character")
167 }
168 _ => self.emit_fatal_diagnostic(
169 "expected expression",
170 token.span,
171 "expression expected here",
172 ),
173 }
174 }
175
176 fn expect(&mut self, token_type: TokenType, message: &str) -> OcelotResult<Token> {
177 let token = self.current().clone();
178
179 if token.token_type != token_type {
180 return self.emit_fatal_diagnostic(message, token.span, "found here");
181 }
182
183 self.position += 1;
184 Ok(token)
185 }
186
187 fn at(&self, token_type: TokenType) -> bool {
188 self.current().token_type == token_type
189 }
190
191 fn current(&self) -> &Token {
192 &self.tokens[self.position]
193 }
194
195 fn source_text(&self, span: &Span) -> &str {
196 &self.source_file.source()[span.start()..span.end()]
197 }
198
199 fn emit_fatal_diagnostic<T>(
200 &mut self,
201 message: impl Into<SharedString>,
202 span: Span,
203 annotation: impl Into<SharedString>,
204 ) -> OcelotResult<T> {
205 self.compilation_context
206 .add_diagnostic(self.source_diagnostic(message, span, annotation));
207 Err(OcelotError::compilation_error(CompilationStage::Parser))
208 }
209
210 fn source_diagnostic(
211 &self,
212 message: impl Into<SharedString>,
213 span: Span,
214 annotation: impl Into<SharedString>,
215 ) -> SourceDiagnostic {
216 let message = message.into();
217 let annotation = annotation.into();
218 let (line_number, line_start, line_end) = self.line_bounds(span.start());
219 let source_line = &self.source_file.source()[line_start..line_end];
220 let relative_start = span.start().saturating_sub(line_start);
221 let relative_end = span.end().saturating_sub(line_start);
222
223 SourceDiagnostic::new(DiagnosticLevel::Error, &self.source_file.path, message).with_excerpt(
224 SourceExcerpt::new(&self.source_file.path, line_number, source_line).with_annotation(
225 SourceAnnotation::new(Span::new(relative_start, relative_end), annotation),
226 ),
227 )
228 }
229
230 fn line_bounds(&self, index: usize) -> (usize, usize, usize) {
231 let source = self.source_file.source();
232 let line_start = source[..index].rfind('\n').map_or(0, |offset| offset + 1);
233 let line_end = source[index..]
234 .find('\n')
235 .map_or(source.len(), |offset| index + offset);
236 let line_number = source[..line_start]
237 .bytes()
238 .filter(|byte| *byte == b'\n')
239 .count()
240 + 1;
241
242 (line_number, line_start, line_end)
243 }
244}
245
246fn is_parser_compilation_error(error: &OcelotError) -> bool {
247 matches!(
248 error.kind(),
249 ErrorKind::CompilationError(CompilationStage::Parser)
250 )
251}