1use crate::BOLD;
2use crate::RED;
3use crate::RESET;
4use crate::compiler::compiler_data::Source;
5use crate::compiler::expr::{Expr, Span, var_assign};
6use crate::compiler::type_system::TypeExpr;
7use crate::errors::BLUE;
8use crate::errors::blue;
9use crate::errors::crash;
10use ariadne::Color;
11use ariadne::Label;
12use ariadne::Report;
13use ariadne::ReportKind;
14use blocks::parse_condition_block;
15use blocks::parse_enum_declare;
16use blocks::parse_eval_block;
17use blocks::parse_for_loop;
18use blocks::parse_function;
19use blocks::parse_impl_block;
20use blocks::parse_loop_block;
21use blocks::parse_match;
22use blocks::parse_struct_declare;
23use blocks::parse_try_catch_block;
24use blocks::parse_while_block;
25use lexer::parse_string;
26use logos::SpannedIter;
27use parser_expr::add_op;
28use parser_expr::parse_expr;
29use smol_strc::SmolStr;
30use std::hint::{cold_path, unreachable_unchecked};
31use std::io::Write;
32use std::iter::Peekable;
33
34use lexer::Token;
35use logos::Logos;
36
37mod blocks;
38mod lexer;
39mod parser_expr;
40mod term;
41
42type TokenIter<'a> = Peekable<SpannedIter<'a, Token<'a>>>;
43
44struct ParserCtx<'a> {
45 src: &'a Source,
46}
47
48struct Parser<'a> {
49 input: TokenIter<'a>,
50 ctx: ParserCtx<'a>,
51 last_token_end: usize,
52}
53
54#[derive(Clone)]
55enum ParserErr<'a> {
56 UnexpectedEOF,
57 UnknownToken,
58 UnexpectedToken(Token<'a>, Token<'a>, &'static str),
60 UnexpectedTokenStr(&'static str, Token<'a>, &'static str),
62 ArrayElementsMissingComma,
63 InlineConditionNoElseBlock,
64 DivisionByZero,
65 ModuloByZero,
66 IntegerNegativeExponent,
67 ArgumentsMissingCommaSeparator,
68 TryBlockNoCatch,
69 MatchBlockNoNonWildcardArm,
70 MatchBlockZeroArms,
71 NestedFunctionDeclaration,
74 LegacyNamespacedImport(String),
77 ImportPathBadExtension,
78}
79
80impl ParserErr<'_> {
81 const fn kind(&self) -> &'static str {
84 match self {
85 ParserErr::UnexpectedEOF => "unexpected_eof",
86 ParserErr::UnknownToken => "unknown_token",
87 ParserErr::UnexpectedToken(..) | ParserErr::UnexpectedTokenStr(..) => {
88 "unexpected_token"
89 }
90 ParserErr::ArrayElementsMissingComma => "array_elements_missing_comma",
91 ParserErr::InlineConditionNoElseBlock => "inline_condition_no_else_block",
92 ParserErr::DivisionByZero => "division_by_zero",
93 ParserErr::ModuloByZero => "modulo_by_zero",
94 ParserErr::IntegerNegativeExponent => "integer_negative_exponent",
95 ParserErr::ArgumentsMissingCommaSeparator => "arguments_missing_comma_separator",
96 ParserErr::TryBlockNoCatch => "try_block_no_catch",
97 ParserErr::MatchBlockNoNonWildcardArm => "match_block_no_non_wildcard_arm",
98 ParserErr::MatchBlockZeroArms => "match_block_zero_arms",
99 ParserErr::NestedFunctionDeclaration => "nested_function_declaration",
100 ParserErr::LegacyNamespacedImport(_) => "legacy_namespaced_import",
101 ParserErr::ImportPathBadExtension => "import_path_bad_extension",
102 }
103 }
104}
105
106#[cold]
107#[inline(never)]
108fn throw_parser_error(src: &Source, Span { start, end }: Span, t: ParserErr) -> ! {
109 let kind = t.kind();
110 let err_message = match t {
111 ParserErr::UnexpectedEOF => "Unexpected EOF",
112 ParserErr::UnknownToken => "Unknown token",
113 ParserErr::UnexpectedToken(expected, received, msg) => &format_args!(
114 "Expected {BLUE}{BOLD}{expected}{RESET}, but got {RED}{BOLD}{received}{RESET}. {msg}"
115 )
116 .to_string(),
117 ParserErr::UnexpectedTokenStr(expected, received, msg) => &format_args!(
118 "Expected {BLUE}{BOLD}{expected}{RESET}, but got {RED}{BOLD}{received}{RESET}. {msg}"
119 )
120 .to_string(),
121 ParserErr::ArrayElementsMissingComma => "Array elements must be separated by a comma",
122 ParserErr::InlineConditionNoElseBlock => "Inline conditions must have an else block",
123 ParserErr::DivisionByZero => "Division by zero",
124 ParserErr::ModuloByZero => "Modulo by zero",
125 ParserErr::IntegerNegativeExponent => "Integers cannot be raised to a negative exponent",
126 ParserErr::ArgumentsMissingCommaSeparator => "Arguments must be separated by a comma",
127 ParserErr::TryBlockNoCatch => {
128 "A {BLUE}{BOLD}try{RESET} block must have at least one {BLUE}{BOLD}catch{RESET} block"
129 }
130 ParserErr::MatchBlockNoNonWildcardArm => {
131 "{BLUE}{BOLD}Match blocks{RESET} must have {BOLD}at least one non-wildcard arm{RESET}"
132 }
133 ParserErr::MatchBlockZeroArms => {
134 "{BLUE}{BOLD}Match blocks{RESET} must have {BOLD}at least one arm{RESET}"
135 }
136 ParserErr::NestedFunctionDeclaration => {
137 "Functions declare at the top level of a file, not inside a block. Move this declaration out of the enclosing block"
138 }
139 ParserErr::LegacyNamespacedImport(path) => &format!(
140 "This import form was removed. Write {BLUE}{BOLD}import \"{path}\";{RESET} instead: a quoted path with no extension imports the library from the shipped library directory"
141 ),
142 ParserErr::ImportPathBadExtension => &format!(
143 "An import path either ends in {BLUE}{BOLD}.cdl{RESET} (a file import) or has no extension (a library import from the shipped library directory)"
144 ),
145 };
146 if crate::errors::diagnostics_enabled() {
147 crate::errors::emit_diagnostic(
148 src.filename.as_str(),
149 (start as usize)..(end as usize),
150 crate::errors::strip_ansi(err_message),
151 kind,
152 );
153 }
154 let mut out = candela_vm::captured_output::stderr();
155 let _ = writeln!(out, "{RED}CANDELA ERROR{RESET}");
156 let report = Report::build(
157 ReportKind::Error,
158 (src.filename.as_str(), (start as usize)..(end as usize)),
159 )
160 .with_label(
161 Label::new((src.filename.as_str(), (start as usize)..(end as usize)))
162 .with_message(err_message)
163 .with_color(Color::Red),
164 )
165 .finish();
166
167 report
168 .write(
169 (
170 src.filename.as_str(),
171 ariadne::Source::from(src.contents.as_str()),
172 ),
173 &mut out,
174 )
175 .unwrap();
176
177 #[cfg(debug_assertions)]
178 panic!();
179
180 #[cfg(not(any(debug_assertions, target_arch = "wasm32", feature = "embed")))]
181 std::process::exit(1);
182
183 #[cfg(target_arch = "wasm32")]
184 wasm_bindgen::throw_str("candela_error");
185
186 #[cfg(all(feature = "embed", not(debug_assertions)))]
187 panic!();
188}
189
190impl<'a> Parser<'a> {
191 #[inline(always)]
192 fn eof_span(&self) -> Span {
193 let end = self.ctx.src.contents.len();
194 (end, end).into()
195 }
196 #[cold]
197 #[inline(never)]
198 fn error(&self, span: Span, error: ParserErr) -> ! {
199 throw_parser_error(self.ctx.src, span, error)
200 }
201 #[inline(always)]
202 fn next_token(&mut self) -> (Token<'a>, Span) {
203 let t = self.input.next().unwrap_or_else(
204 #[cold]
205 || {
206 self.error(self.eof_span(), ParserErr::UnexpectedEOF);
207 },
208 );
209 self.last_token_end = t.1.end;
210 (
211 t.0.unwrap_or_else(
212 #[cold]
213 |()| self.error((t.1.start, t.1.end).into(), ParserErr::UnknownToken),
214 ),
215 (t.1.start, t.1.end).into(),
216 )
217 }
218 #[inline(always)]
219 fn peek_token(&mut self) -> Token<'a> {
220 let Some((t, start, end)) = self
221 .input
222 .peek()
223 .map(|(t, span)| (*t, span.start, span.end))
224 else {
225 self.error(self.eof_span(), ParserErr::UnexpectedEOF);
226 };
227 t.unwrap_or_else(
228 #[cold]
229 |()| self.error((start, end).into(), ParserErr::UnknownToken),
230 )
231 }
232 #[inline(always)]
233 fn peek_token_span(&mut self) -> Span {
234 let Some((_, start, end)) = self
235 .input
236 .peek()
237 .map(|(t, span)| (*t, span.start, span.end))
238 else {
239 self.error(self.eof_span(), ParserErr::UnexpectedEOF);
240 };
241 Span {
242 start: start as u32,
243 end: end as u32,
244 }
245 }
246 #[inline(always)]
247 fn peek_token_opt(&mut self) -> Option<Token<'a>> {
248 let (t, start, end) = self
249 .input
250 .peek()
251 .map(|(t, span)| (*t, span.start, span.end))?;
252 Some(t.unwrap_or_else(
253 #[cold]
254 |()| self.error((start, end).into(), ParserErr::UnknownToken),
255 ))
256 }
257 #[inline(always)]
258 fn peek_token_opt_span(&mut self) -> Option<Span> {
259 self.input
260 .peek()
261 .map(|x| (x.1.start as u32, x.1.end as u32).into())
262 }
263 #[inline(always)]
264 fn next_token_expect(&mut self, expected: Token, msg: &'static str) -> Span {
265 let (next_token, span) = self.next_token();
266 if next_token != expected {
267 self.error(span, ParserErr::UnexpectedToken(expected, next_token, msg));
268 }
269 span
270 }
271 #[inline(always)]
272 fn next_token_expect_closer(
273 &mut self,
274 opener: Token,
275 opener_span: Span,
276 expected_closer: Token,
277 ) -> u32 {
278 if let Some(t) = self.peek_token_opt() {
279 if t == expected_closer {
280 self.next_token().1.end
281 } else {
282 let span = self.peek_token_span();
283 error_unclosed_delimiter(
284 self,
285 opener,
286 opener_span,
287 expected_closer,
288 Some((t, span)),
289 );
290 }
291 } else {
292 error_unclosed_delimiter(self, opener, opener_span, expected_closer, None);
293 }
294 }
295 #[inline(never)]
296 #[cold]
297 pub fn throw_parser_err<'b, F: Fn() -> Report<'b, (&'b str, core::ops::Range<usize>)>>(
298 &self,
299 report: F,
300 span: Span,
301 message: &str,
302 code: &str,
303 ) -> ! {
304 if crate::errors::diagnostics_enabled() {
305 crate::errors::emit_diagnostic(
306 self.ctx.src.filename.as_str(),
307 (span.start as usize)..(span.end as usize),
308 crate::errors::strip_ansi(message),
309 code,
310 );
311 }
312 let report = report();
313
314 report
315 .write(
316 (
317 self.ctx.src.filename.as_str(),
318 ariadne::Source::from(self.ctx.src.contents.as_str()),
319 ),
320 candela_vm::captured_output::stderr(),
321 )
322 .unwrap();
323
324 crash();
325 }
326}
327
328fn parse_namespace(parser: &mut Parser<'_>, initial: SmolStr) -> (Box<[SmolStr]>, u32) {
331 let mut namespace: Vec<SmolStr> = Vec::with_capacity(2);
332 namespace.push(initial);
333 let mut end: u32;
334 loop {
335 let (next_token, span) = parser.next_token();
336 if let Token::Identifier(i) = next_token {
337 namespace.push(SmolStr::new(i));
338 end = span.end;
339 } else {
340 cold_path();
341 parser.error(
342 span,
343 ParserErr::UnexpectedToken(
344 Token::Identifier(""),
345 next_token,
346 "Wrong namespace syntax",
347 ),
348 );
349 }
350 let next_token = parser.peek_token();
351 if next_token == Token::DoubleColon {
352 continue;
353 }
354 return (Box::from(namespace), end);
355 }
356}
357
358fn parse_args(parser: &mut Parser<'_>) -> (Box<[Expr]>, Box<[Span]>, u32) {
360 let mut args = Vec::with_capacity(4);
361 let mut arg_markers: Vec<Span> = Vec::with_capacity(4);
362 loop {
363 if parser.peek_token() == Token::RParen {
364 let end = parser.next_token().1.end;
365 return (Box::from(args), Box::from(arg_markers), end);
366 }
367 let arg_start: u32 = parser.peek_token_span().start;
368 args.push(parse_expr(parser));
369 arg_markers.push((arg_start, parser.peek_token_span().start).into());
370 if parser.peek_token() == Token::Comma {
371 parser.next_token();
372 } else if !(parser.peek_token() == Token::RParen) {
373 cold_path();
374 let span = parser.peek_token_span();
375 parser.error(span, ParserErr::ArgumentsMissingCommaSeparator);
376 }
377 }
378}
379
380fn parse_statement(parser: &mut Parser<'_>) -> Option<Expr> {
381 let token = parser.peek_token_opt()?;
382 let t_span = parser.peek_token_span();
383 match token {
384 Token::If => Some(parse_condition_block(parser, t_span.start)),
385 Token::While => Some(parse_while_block(parser)),
386 Token::For => Some(parse_for_loop(parser)),
387 Token::Match => Some(parse_match(parser)),
388 Token::LBrace => Some(parse_eval_block(parser)),
389 Token::Function => {
393 cold_path();
394 parser.error(t_span, ParserErr::NestedFunctionDeclaration);
395 }
396 Token::Loop => Some(parse_loop_block(parser)),
397 Token::Try => Some(parse_try_catch_block(parser)),
398 Token::Struct => Some(parse_struct_declare(parser)),
399 Token::Enum => Some(parse_enum_declare(parser)),
400 Token::RBrace => None,
401 t => Some(parse_line(parser, t)),
402 }
403}
404
405fn parse_var_declare(parser: &mut Parser<'_>) -> Expr {
406 let (t, _) = parser.next_token();
407 debug_assert_eq!(t, Token::Let);
408 let (t, span) = parser.next_token();
409 let var_name = if let Token::Identifier(id) = t {
410 SmolStr::new(id)
411 } else {
412 cold_path();
413 parser.error(
414 span,
415 ParserErr::UnexpectedToken(
416 Token::Identifier(""),
417 t,
418 "Variable names must be identifiers.",
419 ),
420 );
421 };
422 parser.next_token_expect(
423 Token::Equals,
424 "Variable declarations need a '=' to separate the name from the value.",
425 );
426 let var_value = parse_expr(parser);
427 Expr::VarDeclare(var_name, Box::new(var_value))
428}
429
430fn parse_var_assign(input: &mut Parser<'_>, e: Expr, e_start: u32) -> Expr {
431 let (t, _) = input.next_token();
432 debug_assert_eq!(t, Token::Equals);
433 let e_end = input.peek_token_span().end;
434 let v_start = input.peek_token_span().start;
435 let v = parse_expr(input);
436 let v_end = input.peek_token_span().start;
437 var_assign(e, v, (e_start, e_end).into(), (v_start, v_end).into())
438}
439
440fn parse_op_var_assign(input: &mut Parser<'_>, e: Expr, e_start: u32, op: Token<'_>) -> Expr {
441 let operand_end = input.last_token_end as u32;
442 let (t, _) = input.next_token();
443 debug_assert_eq!(t, op);
444 let e_end = input.peek_token_span().end;
445 let v_start = input.peek_token_span().start;
446 let v = parse_expr(input);
447 let v_end = input.last_token_end as u32;
448 let op = match op {
449 Token::AssignOpAdd => Token::OpAdd,
450 Token::AssignOpSub => Token::OpSub,
451 Token::AssignOpMul => Token::OpMul,
452 Token::AssignOpDiv => Token::OpDiv,
453 Token::AssignOpMod => Token::OpMod,
454 Token::AssignOpPow => Token::OpPow,
455 _ => unsafe { unreachable_unchecked() },
456 };
457 var_assign(
458 e.clone(),
459 add_op(
460 input,
461 op,
462 e,
463 v,
464 (e_start, operand_end).into(),
465 (v_start, v_end).into(),
466 ),
467 (e_start, e_end).into(),
468 (v_start, v_end).into(),
469 )
470}
471
472fn parse_return(input: &mut Parser<'_>) -> Expr {
473 let (t, _) = input.next_token();
474 debug_assert_eq!(t, Token::Return);
475 if input.peek_token_opt() == Some(Token::SemiColon) {
476 Expr::ReturnVal(Box::new(None))
477 } else {
478 let e = parse_expr(input);
479 Expr::ReturnVal(Box::new(Some(e)))
480 }
481}
482
483fn parse_line(input: &mut Parser<'_>, peek: Token<'_>) -> Expr {
484 let line_code = match peek {
485 Token::Let => parse_var_declare(input),
486 Token::Return => parse_return(input),
487 Token::Break => {
488 input.next_token();
489 Expr::Break
490 }
491 Token::Continue => {
492 input.next_token();
493 Expr::Continue
494 }
495 _ => {
496 let e_start = input.peek_token_span().start;
497 let e = parse_expr(input);
498 let peek_token = input.peek_token_opt();
499 match peek_token {
500 Some(Token::Equals) => parse_var_assign(input, e, e_start),
501 Some(
502 op @ (Token::AssignOpAdd
503 | Token::AssignOpSub
504 | Token::AssignOpMul
505 | Token::AssignOpDiv
506 | Token::AssignOpMod
507 | Token::AssignOpPow),
508 ) => parse_op_var_assign(input, e, e_start, op),
509 _ => e,
510 }
511 }
512 };
513 if input.peek_token_opt() != Some(Token::SemiColon) {
514 error_missing_semicolon(input);
515 }
516 input.next_token();
517 line_code
519}
520
521#[cold]
522#[inline(never)]
523fn error_unclosed_delimiter(
524 parser: &Parser<'_>,
525 opener_token: Token,
526 opener_span: Span,
527 expected_closer_token: Token,
528 end: Option<(Token, Span)>,
529) -> ! {
530 let message = if let Some((actual_closer_token, _)) = end {
531 format!(
532 "This {opener_token} is never closed: expected {expected_closer_token} but found {actual_closer_token}"
533 )
534 } else {
535 format!(
536 "This {opener_token} is never closed: expected {expected_closer_token} but the file ends here"
537 )
538 };
539 parser.throw_parser_err(
540 || {
541 let mut report = Report::build(
542 ariadne::ReportKind::Error,
543 (parser.ctx.src.filename.as_str(), opener_span.into()),
544 )
545 .with_message("Unclosed delimiter")
546 .with_label(
547 Label::new((parser.ctx.src.filename.as_str(), opener_span.into()))
548 .with_message(format_args!("This {opener_token} is never closed"))
549 .with_color(ariadne::Color::Red),
550 );
551
552 if let Some((actual_closer_token, actual_closer_token_span)) = end {
553 report = report.with_label(
554 Label::new((
555 parser.ctx.src.filename.as_str(),
556 actual_closer_token_span.into(),
557 ))
558 .with_message(format_args!(
559 "Expected {expected_closer_token} but found {actual_closer_token}"
560 ))
561 .with_color(ariadne::Color::Red),
562 );
563 } else {
564 report = report
565 .with_label(
566 Label::new((parser.ctx.src.filename.as_str(), parser.eof_span().into()))
567 .with_message(format_args!(
568 "Expected {expected_closer_token} but the file ends here"
569 ))
570 .with_color(ariadne::Color::Red),
571 )
572 .with_help(format_args!(
573 "Add a {} here to close it",
574 blue(expected_closer_token)
575 ));
576 }
577
578 report.finish()
579 },
580 opener_span,
581 &message,
582 "unclosed_delimiter",
583 )
584}
585
586#[cold]
587#[inline(never)]
588fn error_missing_semicolon(parser: &Parser<'_>) -> ! {
589 let span: Span = (parser.last_token_end as u32, parser.last_token_end as u32).into();
590 parser.throw_parser_err(
591 || {
592 Report::build(
593 ariadne::ReportKind::Error,
594 (
595 parser.ctx.src.filename.as_str(),
596 (parser.last_token_end..parser.last_token_end),
597 ),
598 )
599 .with_message("Missing semicolon")
600 .with_label(
601 Label::new((
602 parser.ctx.src.filename.as_str(),
603 (parser.last_token_end..parser.last_token_end),
604 ))
605 .with_message(format_args!("Add a {} here", blue(';')))
606 .with_color(ariadne::Color::Blue),
607 )
608 .with_help("All statements end with a ';'")
609 .finish()
610 },
611 span,
612 "Missing semicolon",
613 "missing_semicolon",
614 )
615}
616
617fn parse_code(input: &mut Parser<'_>) -> Vec<Expr> {
618 let mut output: Vec<Expr> = Vec::with_capacity(4);
619 while let Some(e) = parse_statement(input) {
620 output.push(e);
621 }
622 output
623}
624
625fn parse_file_import(parser: &mut Parser<'_>) -> Expr {
626 let (t, Span { start, end: _ }) = parser.next_token();
627 debug_assert_eq!(t, Token::Import);
628 let (next_token, span) = parser.next_token();
629 let (path, is_logical, mut end) = if let Token::String(s) = next_token {
636 let raw = parse_string(s);
637 if raw.ends_with(".cdl") {
638 (raw, false, span.end)
639 } else {
640 let last_segment = raw.rsplit(['/', '\\']).next().unwrap_or(raw.as_str());
641 if last_segment.contains('.') {
642 cold_path();
643 parser.error(span, ParserErr::ImportPathBadExtension);
644 }
645 let mut with_ext = String::with_capacity(raw.len() + 4);
646 with_ext.push_str(raw.as_str());
647 with_ext.push_str(".cdl");
648 (SmolStr::new(&with_ext), true, span.end)
649 }
650 } else if let Token::Identifier(first) = next_token {
651 let mut segments = String::from(first);
654 let mut end = span.end;
655 while parser.peek_token_opt() == Some(Token::DoubleColon) {
656 parser.next_token();
657 let (seg_token, seg_span) = parser.next_token();
658 if let Token::Identifier(seg) = seg_token {
659 segments.push('/');
660 segments.push_str(seg);
661 end = seg_span.end;
662 } else {
663 break;
664 }
665 }
666 cold_path();
667 parser.error(
668 (span.start, end).into(),
669 ParserErr::LegacyNamespacedImport(segments),
670 );
671 } else {
672 cold_path();
673 parser.error(
674 span,
675 ParserErr::UnexpectedToken(
676 Token::String(""),
677 next_token,
678 "An import is a quoted path: import \"./local.cdl\"; for a file, import \"std/string\"; for a library.",
679 ),
680 );
681 };
682 let peek_token = parser.peek_token_opt();
683 if peek_token == Some(Token::As) {
684 parser.next_token();
685 let (next_token, span) = parser.next_token();
686 let alias = if let Token::Identifier(id) = next_token {
687 SmolStr::new(id)
688 } else {
689 cold_path();
690 parser.error(
691 span,
692 ParserErr::UnexpectedToken(
693 Token::Identifier(""),
694 next_token,
695 "Module aliases must be identifiers.",
696 ),
697 );
698 };
699 end = span.end;
700 parser.next_token_expect(
701 Token::SemiColon,
702 "Import statements must end with a semicolon",
703 );
704 Expr::ImportFile(path, Some(alias), is_logical, (start, end).into())
705 } else {
706 parser.next_token_expect(
707 Token::SemiColon,
708 "Import statements must end with a semicolon",
709 );
710 Expr::ImportFile(path, None, is_logical, (start, end).into())
711 }
712}
713
714fn parse_type(parser: &mut Parser<'_>) -> TypeExpr {
715 let t = parse_atomic_type(parser);
716 if parser.peek_token() == Token::Pipe {
717 let mut poly = Vec::with_capacity(2);
718 poly.push(t);
719 while parser.peek_token() == Token::Pipe {
720 parser.next_token();
721 poly.push(parse_atomic_type(parser));
722 }
723 TypeExpr::Union(poly.into_boxed_slice())
724 } else {
725 t
726 }
727}
728
729fn parse_atomic_type(parser: &mut Parser<'_>) -> TypeExpr {
730 let (next_token, span) = parser.next_token();
731 let mut t = if next_token == Token::LBrace {
732 let key_t = parse_type(parser);
733 parser.next_token_expect(
734 Token::Colon,
735 "A colon must separate key and value types in map types",
736 );
737 let value_t = parse_type(parser);
738 parser.next_token_expect(Token::RBrace, "Unmatched '{'");
739 TypeExpr::Map(Box::new(key_t), Box::new(value_t))
740 } else if let Token::Identifier(i) = next_token {
741 if parser.peek_token() == Token::DoubleColon {
742 parser.next_token();
743 let (namespace, end) = parse_namespace(parser, SmolStr::new(i));
744 TypeExpr::NamespacedIdentifier(namespace, (span.start, end).into())
745 } else {
746 TypeExpr::Identifier(SmolStr::new(i), span)
747 }
748 } else {
749 cold_path();
750 parser.error(
751 span,
752 ParserErr::UnexpectedToken(Token::Identifier(""), next_token, "Invalid type"),
753 );
754 };
755 loop {
756 if parser.peek_token() == Token::LBracket {
757 parser.next_token();
758 parser.next_token_expect(Token::RBracket, "Unmatched '['");
759 t = TypeExpr::Array(Box::new(t));
760 } else {
761 break;
762 }
763 }
764 t
765}
766
767fn parse_dylib_import(parser: &mut Parser<'_>) -> Expr {
768 let (t, Span { start, end: _ }) = parser.next_token();
769 debug_assert_eq!(t, Token::Dylib);
770 let (next_token, span) = parser.next_token();
771 let path = if let Token::String(s) = next_token {
772 SmolStr::new(parse_string(s))
773 } else {
774 cold_path();
775 parser.error(
776 span,
777 ParserErr::UnexpectedToken(Token::String(""), next_token, "Paths must be strings."),
778 );
779 };
780 parser.next_token_expect(Token::LBrace, "Blocks need to start with '{'.");
781 let (fn_signatures, end) = parse_fn_signature_block(parser);
782 Expr::ImportDylib(path, fn_signatures, (start, end).into())
783}
784
785fn parse_fn_signature_block(
790 parser: &mut Parser<'_>,
791) -> (Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>, u32) {
792 let mut fn_signatures: Vec<(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)> = Vec::new();
793 let end: u32;
794 loop {
795 if parser.peek_token() == Token::RBrace {
796 end = parser.next_token().1.end;
797 break;
798 }
799
800 let type_start = parser.peek_token();
801 let span = parser.peek_token_span();
802 let first = parse_type(parser);
803 let fn_name_span: Span;
804 let (return_type, fn_name) = if parser.peek_token() == Token::LParen {
805 if let TypeExpr::Identifier(name, span) = first {
806 fn_name_span = span;
807 (
808 TypeExpr::Identifier(SmolStr::new_static("null"), span),
809 name,
810 )
811 } else {
812 parser.error(
813 span,
814 ParserErr::UnexpectedToken(
815 Token::Identifier(""),
816 type_start,
817 "Function names must be identifiers.",
818 ),
819 );
820 }
821 } else {
822 let (next_token, span) = parser.next_token();
823 fn_name_span = span;
824 let fn_name = if let Token::Identifier(name) = next_token {
825 SmolStr::new(name)
826 } else {
827 cold_path();
828 parser.error(
829 span,
830 ParserErr::UnexpectedToken(
831 Token::Identifier(""),
832 next_token,
833 "Function names must be identifiers.",
834 ),
835 );
836 };
837 (first, fn_name)
838 };
839 parser.next_token_expect(
840 Token::LParen,
841 "Function arguments must be delimited by parentheses",
842 );
843 let mut args: Vec<TypeExpr> = Vec::with_capacity(2);
844 loop {
845 if parser.peek_token() == Token::RParen {
846 break;
847 }
848 if parser.peek_token() == Token::Ellipsis {
853 let span = parser.peek_token_span();
854 parser.next_token();
855 args.push(TypeExpr::Identifier(SmolStr::new_static("..."), span));
856 break;
857 }
858 args.push(parse_type(parser));
859 if parser.peek_token() == Token::Comma {
860 parser.next_token();
861 } else if !(parser.peek_token() == Token::RParen) {
862 cold_path();
863 let span = parser.peek_token_span();
864 parser.error(span, ParserErr::ArgumentsMissingCommaSeparator);
865 }
866 }
867 parser.next_token_expect(Token::RParen, "Unmatched ')'");
868 parser.next_token_expect(
869 Token::SemiColon,
870 "Function definitions must end with a semicolon",
871 );
872 fn_signatures.push((fn_name, Box::from(args), return_type, fn_name_span));
873 }
874 (fn_signatures.into_boxed_slice(), end)
875}
876
877fn parse_host_block(parser: &mut Parser<'_>) -> Expr {
881 let (t, Span { start, end: _ }) = parser.next_token();
882 debug_assert_eq!(t, Token::Host);
883 let (next_token, span) = parser.next_token();
884 let namespace = if let Token::String(s) = next_token {
885 SmolStr::new(parse_string(s))
886 } else {
887 cold_path();
888 parser.error(
889 span,
890 ParserErr::UnexpectedToken(
891 Token::String(""),
892 next_token,
893 "Host namespaces must be strings.",
894 ),
895 );
896 };
897 parser.next_token_expect(Token::LBrace, "Blocks need to start with '{'.");
898 let (fn_signatures, end) = parse_fn_signature_block(parser);
899 Expr::HostBlock(namespace, fn_signatures, (start, end).into())
900}
901
902#[inline(always)]
903fn parse_file(parser: &mut Parser<'_>) -> Vec<Expr> {
904 let mut output: Vec<Expr> = Vec::with_capacity(2);
905 while let Some(t) = parser.peek_token_opt() {
907 if t == Token::Impl {
910 parse_impl_block(parser, &mut output);
911 continue;
912 }
913 output.push(match t {
914 Token::Function => parse_function(parser),
915 Token::Import => parse_file_import(parser),
916 Token::Struct => parse_struct_declare(parser),
917 Token::Enum => parse_enum_declare(parser),
918 Token::Dylib => parse_dylib_import(parser),
919 Token::Host => parse_host_block(parser),
920 unexpected => {
921 cold_path();
922 let span = parser.peek_token_span();
923 parser.error(span, ParserErr::UnexpectedTokenStr("'fn' (function declaration), 'import', 'struct' (struct declaration), 'enum' (enum declaration), 'impl' (method block), 'dylib' (dynamic library import), or 'host' (host function block)", unexpected, "Invalid file statement."));
924 }
925 });
926 }
927 output
928}
929
930#[must_use]
931pub fn parse(input: &str, src: &Source) -> Vec<Expr> {
932 parse_file(&mut Parser {
933 input: Token::lexer(input).spanned().peekable(),
934 ctx: ParserCtx { src },
935 last_token_end: 0,
936 })
937}