1use crate::BOLD;
5use crate::RED;
6use crate::RESET;
7use crate::cfg;
8use crate::cfg::Cfg;
9use crate::compiler::compiler_data::Source;
10use crate::compiler::expr::OPERATOR_SYMBOLS;
11use crate::compiler::expr::{Expr, ImportBinding, LetAnnotation, Span, var_assign};
12use crate::compiler::type_system::FnTypeExpr;
13use crate::compiler::type_system::GenericType;
14use crate::compiler::type_system::ImplTemplate;
15use crate::compiler::type_system::TypeExpr;
16use crate::compiler::type_system::TypeParams;
17use crate::errors::BLUE;
18use crate::errors::blue;
19use crate::errors::crash;
20use crate::macros;
21use crate::macros::Expansion;
22use crate::macros::MAX_EXPANSION_DEPTH;
23use ariadne::Color;
24use ariadne::Label;
25use ariadne::Report;
26use ariadne::ReportKind;
27use blocks::parse_condition_block;
28use blocks::parse_enum_declare;
29use blocks::parse_eval_block;
30use blocks::parse_for_loop;
31use blocks::parse_function;
32use blocks::parse_impl_block;
33use blocks::parse_loop_block;
34use blocks::parse_match;
35use blocks::parse_struct_declare;
36use blocks::parse_try_catch_block;
37use blocks::parse_while_block;
38use lexer::LexErr;
39use lexer::MacroToken;
40use lexer::parse_string;
41use logos::SpannedIter;
42use parser_expr::add_op;
43use parser_expr::parse_expr;
44use smol_strc::SmolStr;
45use std::hint::{cold_path, unreachable_unchecked};
46use std::io::Write;
47use std::iter::Peekable;
48
49use lexer::Token;
50use logos::Logos;
51
52mod blocks;
53mod lexer;
54mod parser_expr;
55mod term;
56
57type TokenIter<'a> = Peekable<SpannedIter<'a, Token<'a>>>;
58
59struct ParserCtx<'a> {
60 src: &'a Source,
61 cfg: &'a Cfg,
63}
64
65struct Parser<'a> {
66 input: TokenIter<'a>,
67 ctx: ParserCtx<'a>,
68 last_token_end: usize,
69 span_override: Option<Span>,
74 nesting: u32,
78 pending_gt: Option<Span>,
83 inactive: u32,
88}
89
90pub const MAX_NESTING_DEPTH: u32 = 128;
97
98#[derive(Clone)]
99enum ParserErr<'a> {
100 UnexpectedEOF,
101 UnknownToken,
102 IntLiteralOutOfRange,
104 FloatLiteralOutOfRange,
106 FloatExponentMissingDigits,
108 UnexpectedToken(Token<'a>, Token<'a>, &'static str),
110 UnexpectedTokenStr(&'static str, Token<'a>, &'static str),
112 ArrayElementsMissingComma,
113 InlineConditionNoElseBlock,
114 DivisionByZero,
115 ModuloByZero,
116 IntegerNegativeExponent,
117 ShiftCountOutOfRange(i64),
120 ArgumentsMissingCommaSeparator,
121 TryBlockNoCatch,
122 MatchBlockNoNonWildcardArm,
123 MatchBlockZeroArms,
124 MatchArmMissingComma,
127 MatchWildcardAlternative,
130 NestedFunctionDeclaration,
133 LegacyNamespacedImport(String),
136 ImportPathBadExtension,
137 ImportNameNotIdentifier(String),
140 ImportAliasWithItems,
142 ImportItemsEmpty,
144 UnterminatedMacroRegion(&'a str),
146 UnknownMacro(&'a str),
148 MacroExpansionFailed(&'a str, String),
150 MacroExpansionTrailingTokens(&'a str),
152 MacroRecursionLimit(&'a str),
154 NestingTooDeep,
156 OperatorMethodUnknown(&'static str),
159 UnknownAttribute(&'a str),
161 AttributeWithoutItem,
163 ThrowCall,
165}
166
167impl ParserErr<'_> {
168 const fn kind(&self) -> &'static str {
171 match self {
172 ParserErr::UnexpectedEOF => "unexpected_eof",
173 ParserErr::UnknownToken => "unknown_token",
174 ParserErr::IntLiteralOutOfRange => "int_literal_out_of_range",
175 ParserErr::FloatLiteralOutOfRange => "float_literal_out_of_range",
176 ParserErr::FloatExponentMissingDigits => "float_exponent_missing_digits",
177 ParserErr::UnexpectedToken(..) | ParserErr::UnexpectedTokenStr(..) => {
178 "unexpected_token"
179 }
180 ParserErr::ArrayElementsMissingComma => "array_elements_missing_comma",
181 ParserErr::InlineConditionNoElseBlock => "inline_condition_no_else_block",
182 ParserErr::DivisionByZero => "division_by_zero",
183 ParserErr::ModuloByZero => "modulo_by_zero",
184 ParserErr::IntegerNegativeExponent => "integer_negative_exponent",
185 ParserErr::ShiftCountOutOfRange(_) => "shift_count_out_of_range",
186 ParserErr::ArgumentsMissingCommaSeparator => "arguments_missing_comma_separator",
187 ParserErr::TryBlockNoCatch => "try_block_no_catch",
188 ParserErr::MatchBlockNoNonWildcardArm => "match_block_no_non_wildcard_arm",
189 ParserErr::MatchBlockZeroArms => "match_block_zero_arms",
190 ParserErr::MatchArmMissingComma => "match_arm_missing_comma",
191 ParserErr::MatchWildcardAlternative => "match_wildcard_alternative",
192 ParserErr::NestedFunctionDeclaration => "nested_function_declaration",
193 ParserErr::LegacyNamespacedImport(_) => "legacy_namespaced_import",
194 ParserErr::ImportPathBadExtension => "import_path_bad_extension",
195 ParserErr::ImportNameNotIdentifier(_) => "import_name_not_identifier",
196 ParserErr::ImportAliasWithItems => "import_alias_with_items",
197 ParserErr::ImportItemsEmpty => "import_items_empty",
198 ParserErr::UnterminatedMacroRegion(_) => "unterminated_macro_region",
199 ParserErr::UnknownMacro(_) => "unknown_macro",
200 ParserErr::MacroExpansionFailed(..) => "macro_expansion_failed",
201 ParserErr::MacroExpansionTrailingTokens(_) => "macro_expansion_trailing_tokens",
202 ParserErr::MacroRecursionLimit(_) => "macro_recursion_limit",
203 ParserErr::NestingTooDeep => "nesting_too_deep",
204 ParserErr::OperatorMethodUnknown(_) => "operator_method_unknown",
205 ParserErr::UnknownAttribute(_) => "unknown_attribute",
206 ParserErr::AttributeWithoutItem => "attribute_without_item",
207 ParserErr::ThrowCall => "throw_call",
208 }
209 }
210}
211
212const fn lex_err(e: LexErr) -> ParserErr<'static> {
215 match e {
216 LexErr::UnknownToken => ParserErr::UnknownToken,
217 LexErr::IntOutOfRange => ParserErr::IntLiteralOutOfRange,
218 LexErr::FloatOutOfRange => ParserErr::FloatLiteralOutOfRange,
219 LexErr::FloatExponentMissingDigits => ParserErr::FloatExponentMissingDigits,
220 }
221}
222
223#[cold]
224#[inline(never)]
225fn throw_parser_error(src: &Source, Span { start, end }: Span, t: ParserErr) -> ! {
226 let kind = t.kind();
227 let err_message = match t {
228 ParserErr::UnexpectedEOF => "Unexpected EOF",
229 ParserErr::UnknownToken => "Unknown token",
230 ParserErr::IntLiteralOutOfRange => &format!(
231 "This number does not fit an {BLUE}{BOLD}int{RESET}, which holds {} to {}. A wider value goes in a {BLUE}{BOLD}float{RESET}",
232 i64::MIN,
233 i64::MAX
234 ),
235 ParserErr::FloatLiteralOutOfRange => &format!(
236 "This number does not fit a {BLUE}{BOLD}float{RESET}, which holds {:e} to {:e}",
237 f64::MIN,
238 f64::MAX
239 ),
240 ParserErr::FloatExponentMissingDigits => {
241 "This exponent has no digits after it. Write the power, as in 1e3 or 2.5e-1"
242 }
243 ParserErr::UnexpectedToken(expected, received, msg) => &format_args!(
244 "Expected {BLUE}{BOLD}{expected}{RESET}, but got {RED}{BOLD}{received}{RESET}. {msg}"
245 )
246 .to_string(),
247 ParserErr::UnexpectedTokenStr(expected, received, msg) => &format_args!(
248 "Expected {BLUE}{BOLD}{expected}{RESET}, but got {RED}{BOLD}{received}{RESET}. {msg}"
249 )
250 .to_string(),
251 ParserErr::ArrayElementsMissingComma => "Array elements must be separated by a comma",
252 ParserErr::InlineConditionNoElseBlock => "Inline conditions must have an else block",
253 ParserErr::DivisionByZero => "Division by zero",
254 ParserErr::ModuloByZero => "Modulo by zero",
255 ParserErr::IntegerNegativeExponent => "Integers cannot be raised to a negative exponent",
256 ParserErr::ShiftCountOutOfRange(count) => &format_args!(
257 "An {BLUE}{BOLD}int{RESET} is 64 bits wide, so it cannot be shifted by {RED}{BOLD}{count}{RESET}. The count must be between 0 and 63"
258 )
259 .to_string(),
260 ParserErr::ArgumentsMissingCommaSeparator => "Arguments must be separated by a comma",
261 ParserErr::TryBlockNoCatch => {
262 "A {BLUE}{BOLD}try{RESET} block must have at least one {BLUE}{BOLD}catch{RESET} block"
263 }
264 ParserErr::MatchBlockNoNonWildcardArm => &format!(
265 "{BLUE}{BOLD}Match blocks{RESET} must have {BOLD}at least one non-wildcard arm{RESET}"
266 ),
267 ParserErr::MatchBlockZeroArms => &format!(
268 "{BLUE}{BOLD}Match blocks{RESET} must have {BOLD}at least one arm{RESET}"
269 ),
270 ParserErr::MatchArmMissingComma => &format!(
271 "An arm written {BLUE}{BOLD}pattern => expression{RESET} ends with a {BOLD}','{RESET} before the next arm"
272 ),
273 ParserErr::MatchWildcardAlternative => &format!(
274 "{BLUE}{BOLD}_{RESET} stands alone as the last arm of a match, not as one of a pattern's {BOLD}|{RESET} alternatives"
275 ),
276 ParserErr::ThrowCall => &format!(
277 "{BLUE}{BOLD}throw{RESET} is a statement, not a function. Write {BLUE}{BOLD}throw \"kind\";{RESET} with no parentheses"
278 ),
279 ParserErr::NestedFunctionDeclaration => {
280 "Functions declare at the top level of a file, not inside a block. Move this declaration out of the enclosing block"
281 }
282 ParserErr::LegacyNamespacedImport(path) => &format!(
283 "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"
284 ),
285 ParserErr::ImportPathBadExtension => &format!(
286 "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)"
287 ),
288 ParserErr::ImportNameNotIdentifier(segment) => &format!(
289 "An import binds the module under the last segment of its path, and {RED}{BOLD}{segment}{RESET} is not a name. Give it one with {BLUE}{BOLD}as{RESET}: {BLUE}{BOLD}import \"...\" as name;{RESET}"
290 ),
291 ParserErr::ImportAliasWithItems => &format!(
292 "An import either binds the module under a name ({BLUE}{BOLD}as name{RESET}) or names the items it brings in ({BLUE}{BOLD}{{ a, b }}{RESET}), not both. Write two imports of the same path"
293 ),
294 ParserErr::ImportItemsEmpty => &format!(
295 "Name at least one item between the braces, or drop them to bind the module: {BLUE}{BOLD}import \"std/json\";{RESET}"
296 ),
297 ParserErr::UnterminatedMacroRegion(name) => &format!(
298 "This {BLUE}{BOLD}{name}!{RESET} region is never closed: the file ends before its ')'"
299 ),
300 ParserErr::UnknownMacro(name) => &format!(
301 "No macro named {RED}{BOLD}{name}!{RESET} is registered. Macros come from the program embedding candela, which decides what each one means"
302 ),
303 ParserErr::MacroExpansionFailed(name, message) => {
304 &format!("The {BLUE}{BOLD}{name}!{RESET} macro rejected this region: {message}")
305 }
306 ParserErr::MacroExpansionTrailingTokens(name) => &format!(
307 "The {BLUE}{BOLD}{name}!{RESET} macro expanded to more than one expression. A macro stands where an expression stands, so it expands to exactly one"
308 ),
309 ParserErr::MacroRecursionLimit(name) => &format!(
310 "Expanding this macro reached {RED}{BOLD}{name}!{RESET} more than {MAX_EXPANSION_DEPTH} macros deep. An expansion may use another macro, but the chain has to end"
311 ),
312 ParserErr::NestingTooDeep => &format!(
313 "This is nested more than {MAX_NESTING_DEPTH} levels deep. Move the inner part into a variable or a function"
314 ),
315 ParserErr::OperatorMethodUnknown(symbol) => &format!(
316 "A type does not define {RED}{BOLD}{symbol}{RESET}. The operators it defines are {BLUE}{BOLD}{OPERATOR_SYMBOLS}{RESET}{}",
317 match symbol {
318 "!=" => ", and != comes from the == it defines",
319 ">" => ", and > comes from the < it defines",
320 ">=" => ", and >= comes from the <= it defines",
321 _ => "",
322 }
323 ),
324 ParserErr::UnknownAttribute(name) => &format!(
325 "There is no attribute named {RED}{BOLD}{name}{RESET}. The attribute candela has is {BLUE}{BOLD}@cfg(...){RESET}"
326 ),
327 ParserErr::AttributeWithoutItem => {
328 "An attribute applies to the declaration or statement after it, and nothing follows this one"
329 }
330 };
331 if crate::errors::diagnostics_enabled() {
332 crate::errors::emit_diagnostic(
333 src.filename.as_str(),
334 (start as usize)..(end as usize),
335 crate::errors::strip_ansi(err_message),
336 kind,
337 );
338 }
339 let mut out = candela_vm::captured_output::stderr();
340 let _ = writeln!(out, "{RED}CANDELA ERROR{RESET}");
341 let report = Report::build(
342 ReportKind::Error,
343 (src.filename.as_str(), (start as usize)..(end as usize)),
344 )
345 .with_label(
346 Label::new((src.filename.as_str(), (start as usize)..(end as usize)))
347 .with_message(err_message)
348 .with_color(Color::Red),
349 )
350 .finish();
351
352 let _ = report.write(
355 (
356 src.filename.as_str(),
357 ariadne::Source::from(src.contents.as_str()),
358 ),
359 &mut out,
360 );
361
362 #[cfg(debug_assertions)]
363 panic!();
364
365 #[cfg(not(any(debug_assertions, target_arch = "wasm32", feature = "embed")))]
366 std::process::exit(1);
367
368 #[cfg(target_arch = "wasm32")]
369 wasm_bindgen::throw_str("candela_error");
370
371 #[cfg(all(feature = "embed", not(debug_assertions)))]
372 panic!();
373}
374
375impl<'a> Parser<'a> {
376 #[inline(always)]
379 const fn placed(&self, span: Span) -> Span {
380 match self.span_override {
381 Some(macro_span) => macro_span,
382 None => span,
383 }
384 }
385 #[inline(always)]
386 fn eof_span(&self) -> Span {
387 let end = self.ctx.src.contents.len();
388 self.placed((end, end).into())
389 }
390 #[cold]
391 #[inline(never)]
392 fn error(&self, span: Span, error: ParserErr) -> ! {
393 throw_parser_error(self.ctx.src, span, error)
394 }
395 #[inline(always)]
398 fn enter(&mut self, span: Span) {
399 if self.nesting >= MAX_NESTING_DEPTH {
400 cold_path();
401 self.error(span, ParserErr::NestingTooDeep);
402 }
403 self.nesting += 1;
404 }
405 #[inline(always)]
406 const fn leave(&mut self) {
407 self.nesting -= 1;
408 }
409 #[inline(always)]
410 fn next_token(&mut self) -> (Token<'a>, Span) {
411 if let Some(span) = self.pending_gt.take() {
412 let span = self.placed(span);
413 self.last_token_end = span.end as usize;
414 return (Token::OpSup, span);
415 }
416 let t = self.input.next().unwrap_or_else(
417 #[cold]
418 || {
419 self.error(self.eof_span(), ParserErr::UnexpectedEOF);
420 },
421 );
422 let span = self.placed((t.1.start, t.1.end).into());
423 self.last_token_end = span.end as usize;
424 (
425 t.0.unwrap_or_else(
426 #[cold]
427 |e| self.error(span, lex_err(e)),
428 ),
429 span,
430 )
431 }
432 #[inline(always)]
433 fn peek_token(&mut self) -> Token<'a> {
434 if self.pending_gt.is_some() {
435 return Token::OpSup;
436 }
437 let Some((t, start, end)) = self
438 .input
439 .peek()
440 .map(|(t, span)| (*t, span.start, span.end))
441 else {
442 self.error(self.eof_span(), ParserErr::UnexpectedEOF);
443 };
444 t.unwrap_or_else(
445 #[cold]
446 |e| self.error(self.placed((start, end).into()), lex_err(e)),
447 )
448 }
449 #[inline(always)]
450 fn peek_token_span(&mut self) -> Span {
451 if let Some(span) = self.pending_gt {
452 return self.placed(span);
453 }
454 let Some((_, start, end)) = self
455 .input
456 .peek()
457 .map(|(t, span)| (*t, span.start, span.end))
458 else {
459 self.error(self.eof_span(), ParserErr::UnexpectedEOF);
460 };
461 self.placed(Span {
462 start: start as u32,
463 end: end as u32,
464 })
465 }
466 #[inline(always)]
467 fn peek_token_opt(&mut self) -> Option<Token<'a>> {
468 if self.pending_gt.is_some() {
469 return Some(Token::OpSup);
470 }
471 let (t, start, end) = self
472 .input
473 .peek()
474 .map(|(t, span)| (*t, span.start, span.end))?;
475 Some(t.unwrap_or_else(
476 #[cold]
477 |e| self.error(self.placed((start, end).into()), lex_err(e)),
478 ))
479 }
480 #[inline(always)]
481 fn peek_token_opt_span(&mut self) -> Option<Span> {
482 if let Some(span) = self.pending_gt {
483 return Some(self.placed(span));
484 }
485 let span = self
486 .input
487 .peek()
488 .map(|x| (x.1.start as u32, x.1.end as u32).into())?;
489 Some(self.placed(span))
490 }
491 #[inline(always)]
492 fn next_token_expect(&mut self, expected: Token, msg: &'static str) -> Span {
493 let (next_token, span) = self.next_token();
494 if next_token != expected {
495 self.error(span, ParserErr::UnexpectedToken(expected, next_token, msg));
496 }
497 span
498 }
499 fn next_type_list_close(&mut self, msg: &'static str) -> Span {
506 if self.peek_token_opt() == Some(Token::OpShr) {
507 let (_, span) = self.next_token();
508 let middle = span.start + 1;
509 self.pending_gt = Some((middle, span.end).into());
510 self.last_token_end = middle as usize;
511 return (span.start, middle).into();
512 }
513 self.next_token_expect(Token::OpSup, msg)
514 }
515 #[inline(always)]
516 fn next_token_expect_closer(
517 &mut self,
518 opener: Token,
519 opener_span: Span,
520 expected_closer: Token,
521 ) -> u32 {
522 if let Some(t) = self.peek_token_opt() {
523 if t == expected_closer {
524 self.next_token().1.end
525 } else {
526 let span = self.peek_token_span();
527 error_unclosed_delimiter(
528 self,
529 opener,
530 opener_span,
531 expected_closer,
532 Some((t, span)),
533 );
534 }
535 } else {
536 error_unclosed_delimiter(self, opener, opener_span, expected_closer, None);
537 }
538 }
539 #[inline(never)]
540 #[cold]
541 pub fn throw_parser_err<'b, F: Fn() -> Report<'b, (&'b str, core::ops::Range<usize>)>>(
542 &self,
543 report: F,
544 span: Span,
545 message: &str,
546 code: &str,
547 ) -> ! {
548 if crate::errors::diagnostics_enabled() {
549 crate::errors::emit_diagnostic(
550 self.ctx.src.filename.as_str(),
551 (span.start as usize)..(span.end as usize),
552 crate::errors::strip_ansi(message),
553 code,
554 );
555 }
556 let report = report();
557
558 let _ = report.write(
559 (
560 self.ctx.src.filename.as_str(),
561 ariadne::Source::from(self.ctx.src.contents.as_str()),
562 ),
563 candela_vm::captured_output::stderr(),
564 );
565
566 crash();
567 }
568}
569
570fn parse_namespace(parser: &mut Parser<'_>, initial: SmolStr) -> (Box<[SmolStr]>, u32) {
573 let mut namespace: Vec<SmolStr> = Vec::with_capacity(2);
574 namespace.push(initial);
575 let end = extend_namespace(parser, &mut namespace);
576 (Box::from(namespace), end)
577}
578
579fn extend_namespace(parser: &mut Parser<'_>, namespace: &mut Vec<SmolStr>) -> u32 {
584 let mut end: u32;
585 loop {
586 let (next_token, span) = parser.next_token();
587 if let Token::Identifier(i) = next_token {
588 namespace.push(SmolStr::new(i));
589 end = span.end;
590 } else {
591 cold_path();
592 parser.error(
593 span,
594 ParserErr::UnexpectedToken(
595 Token::Identifier(""),
596 next_token,
597 "Wrong namespace syntax",
598 ),
599 );
600 }
601 if parser.peek_token() == Token::DoubleColon {
602 parser.next_token();
606 continue;
607 }
608 return end;
609 }
610}
611
612fn parse_args(parser: &mut Parser<'_>) -> (Box<[Expr]>, Box<[Span]>, u32) {
614 let mut args = Vec::with_capacity(4);
615 let mut arg_markers: Vec<Span> = Vec::with_capacity(4);
616 loop {
617 if parser.peek_token() == Token::RParen {
618 let end = parser.next_token().1.end;
619 return (Box::from(args), Box::from(arg_markers), end);
620 }
621 let arg_start: u32 = parser.peek_token_span().start;
622 args.push(parse_expr(parser));
623 arg_markers.push((arg_start, parser.peek_token_span().start).into());
624 if parser.peek_token() == Token::Comma {
625 parser.next_token();
626 } else if !(parser.peek_token() == Token::RParen) {
627 cold_path();
628 let span = parser.peek_token_span();
629 parser.error(span, ParserErr::ArgumentsMissingCommaSeparator);
630 }
631 }
632}
633
634fn expand_macro(parser: &Parser<'_>, region: MacroToken<'_>, span: Span) -> Expr {
637 let Some(body) = region.body else {
638 cold_path();
639 parser.error(span, ParserErr::UnterminatedMacroRegion(region.name));
640 };
641 if parser.inactive > 0 {
644 return Expr::Null;
645 }
646 let Some(_level) = macros::Depth::enter() else {
650 parser.error(span, ParserErr::MacroRecursionLimit(region.name));
651 };
652 match macros::expand(region.name, body) {
653 Expansion::Text(expansion) => parse_expansion(parser, region.name, &expansion, span),
654 Expansion::Null => Expr::Null,
655 Expansion::Unknown => {
656 cold_path();
657 parser.error(span, ParserErr::UnknownMacro(region.name));
658 }
659 Expansion::Failed(error) => {
660 cold_path();
661 let body_start = span.start + region.name.len() as u32 + 2;
664 let error_span = match error.offset {
665 Some(offset) => {
666 let start = body_start + (offset.min(body.len()) as u32);
667 (start, (start + 1).min(body_start + body.len() as u32)).into()
668 }
669 None => span,
670 };
671 parser.error(
672 error_span,
673 ParserErr::MacroExpansionFailed(region.name, error.message),
674 );
675 }
676 }
677}
678
679fn parse_expansion(parser: &Parser<'_>, macro_name: &str, expansion: &str, span: Span) -> Expr {
684 let mut sub = Parser {
685 input: Token::lexer(expansion).spanned().peekable(),
686 ctx: ParserCtx {
687 src: parser.ctx.src,
688 cfg: parser.ctx.cfg,
689 },
690 last_token_end: span.end as usize,
691 span_override: Some(span),
692 nesting: parser.nesting,
693 pending_gt: None,
694 inactive: parser.inactive,
695 };
696 let expr = parse_expr(&mut sub);
697 if sub.peek_token_opt().is_some() {
698 cold_path();
699 sub.error(span, ParserErr::MacroExpansionTrailingTokens(macro_name));
700 }
701 expr
702}
703
704#[must_use]
710pub fn catch_kinds_and_strings(src: &str) -> (Vec<(SmolStr, Span)>, Vec<SmolStr>) {
711 let mut kinds = Vec::new();
712 let mut strings = Vec::new();
713 let mut after_catch = false;
714 for (token, range) in Token::lexer(src).spanned() {
715 match token {
716 Ok(Token::String(s)) if after_catch => {
717 kinds.push((
718 lexer::parse_string(s),
719 (range.start as u32, range.end as u32).into(),
720 ));
721 }
722 Ok(Token::String(s)) => strings.push(lexer::parse_string(s)),
723 _ => {}
724 }
725 after_catch = matches!(token, Ok(Token::Catch));
726 }
727 (kinds, strings)
728}
729
730fn parse_statement(parser: &mut Parser<'_>) -> Option<Expr> {
731 let mut token = parser.peek_token_opt()?;
732 while token == Token::At {
735 let active = parse_attributes(parser);
736 if matches!(parser.peek_token_opt(), None | Some(Token::RBrace)) {
737 cold_path();
738 let span = parser
739 .peek_token_opt_span()
740 .unwrap_or_else(|| parser.eof_span());
741 parser.error(span, ParserErr::AttributeWithoutItem);
742 }
743 if active {
744 return parse_statement(parser);
745 }
746 parser.inactive += 1;
747 let _ = parse_statement(parser);
748 parser.inactive -= 1;
749 token = parser.peek_token_opt()?;
750 }
751 let t_span = parser.peek_token_span();
752 match token {
753 Token::If => Some(parse_condition_block(parser, t_span.start)),
754 Token::While => Some(parse_while_block(parser)),
755 Token::For => Some(parse_for_loop(parser)),
756 Token::Match => Some(parse_match(parser)),
757 Token::LBrace => Some(parse_eval_block(parser)),
758 Token::Function => {
762 cold_path();
763 parser.error(t_span, ParserErr::NestedFunctionDeclaration);
764 }
765 Token::Loop => Some(parse_loop_block(parser)),
766 Token::Try => Some(parse_try_catch_block(parser)),
767 Token::Struct => Some(parse_struct_declare(parser)),
768 Token::Enum => Some(parse_enum_declare(parser)),
769 Token::RBrace => None,
770 t => Some(parse_line(parser, t)),
771 }
772}
773
774fn parse_var_declare(parser: &mut Parser<'_>) -> Expr {
775 let (t, _) = parser.next_token();
776 debug_assert_eq!(t, Token::Let);
777 let (t, span) = parser.next_token();
778 let var_name = if let Token::Identifier(id) = t {
779 SmolStr::new(id)
780 } else {
781 cold_path();
782 parser.error(
783 span,
784 ParserErr::UnexpectedToken(
785 Token::Identifier(""),
786 t,
787 "Variable names must be identifiers.",
788 ),
789 );
790 };
791 let annotation = if parser.peek_token_opt() == Some(Token::Colon) {
793 parser.next_token();
794 let start = parser.peek_token_span().start;
795 let ty = parse_type(parser);
796 Some((ty, Span::from((start, parser.last_token_end as u32))))
797 } else {
798 None
799 };
800 parser.next_token_expect(
801 Token::Equals,
802 "Variable declarations need a '=' to separate the name from the value.",
803 );
804 let value_start = parser.peek_token_span().start;
805 let var_value = parse_expr(parser);
806 let value_span = Span::from((value_start, parser.last_token_end as u32));
807 Expr::VarDeclare(
808 var_name,
809 Box::new(var_value),
810 annotation.map(|(ty, span)| {
811 Box::new(LetAnnotation {
812 ty,
813 span,
814 value_span,
815 })
816 }),
817 )
818}
819
820fn parse_var_assign(input: &mut Parser<'_>, e: Expr, e_start: u32) -> Expr {
821 let (t, _) = input.next_token();
822 debug_assert_eq!(t, Token::Equals);
823 let e_end = input.peek_token_span().end;
824 let v_start = input.peek_token_span().start;
825 let v = parse_expr(input);
826 let v_end = input.peek_token_span().start;
827 var_assign(e, v, (e_start, e_end).into(), (v_start, v_end).into())
828}
829
830fn parse_op_var_assign(input: &mut Parser<'_>, e: Expr, e_start: u32, op: Token<'_>) -> Expr {
831 let operand_end = input.last_token_end as u32;
832 let (t, _) = input.next_token();
833 debug_assert_eq!(t, op);
834 let e_end = input.peek_token_span().end;
835 let v_start = input.peek_token_span().start;
836 let v = parse_expr(input);
837 let v_end = input.last_token_end as u32;
838 let op = match op {
839 Token::AssignOpAdd => Token::OpAdd,
840 Token::AssignOpSub => Token::OpSub,
841 Token::AssignOpMul => Token::OpMul,
842 Token::AssignOpDiv => Token::OpDiv,
843 Token::AssignOpMod => Token::OpMod,
844 Token::AssignOpPow => Token::OpPow,
845 Token::AssignOpBitAnd => Token::OpBitAnd,
846 Token::AssignOpBitOr => Token::Pipe,
847 Token::AssignOpBitXor => Token::OpBitXor,
848 Token::AssignOpShl => Token::OpShl,
849 Token::AssignOpShr => Token::OpShr,
850 _ => unsafe { unreachable_unchecked() },
851 };
852 var_assign(
853 e.clone(),
854 add_op(
855 input,
856 op,
857 e,
858 v,
859 (e_start, operand_end).into(),
860 (v_start, v_end).into(),
861 ),
862 (e_start, e_end).into(),
863 (v_start, v_end).into(),
864 )
865}
866
867fn parse_return(input: &mut Parser<'_>) -> Expr {
868 let (t, _) = input.next_token();
869 debug_assert_eq!(t, Token::Return);
870 if input.peek_token_opt() == Some(Token::SemiColon) {
871 Expr::ReturnVal(Box::new(None))
872 } else {
873 let e = parse_expr(input);
874 Expr::ReturnVal(Box::new(Some(e)))
875 }
876}
877
878fn parse_throw(input: &mut Parser<'_>) -> Expr {
882 let (t, throw_span) = input.next_token();
883 debug_assert_eq!(t, Token::Throw);
884 if input.peek_token_opt() == Some(Token::LParen) {
885 cold_path();
886 input.error(throw_span, ParserErr::ThrowCall);
887 }
888 let start = input.peek_token_span().start;
889 let kind = parse_expr(input);
890 let end = input.last_token_end as u32;
891 Expr::FunctionCall(
892 Box::new([kind]),
893 Box::from([SmolStr::new_static("throw")]),
894 throw_span.extend((start, end).into()),
895 Box::from([Span::from((start, end))]),
896 Box::from([]),
897 )
898}
899
900fn parse_line(input: &mut Parser<'_>, peek: Token<'_>) -> Expr {
901 let line_code = match peek {
902 Token::Let => parse_var_declare(input),
903 Token::Return => parse_return(input),
904 Token::Throw => parse_throw(input),
905 Token::Break => {
906 input.next_token();
907 Expr::Break
908 }
909 Token::Continue => {
910 input.next_token();
911 Expr::Continue
912 }
913 _ => {
914 let e_start = input.peek_token_span().start;
915 let e = parse_expr(input);
916 let peek_token = input.peek_token_opt();
917 match peek_token {
918 Some(Token::Equals) => parse_var_assign(input, e, e_start),
919 Some(
920 op @ (Token::AssignOpAdd
921 | Token::AssignOpSub
922 | Token::AssignOpMul
923 | Token::AssignOpDiv
924 | Token::AssignOpMod
925 | Token::AssignOpPow
926 | Token::AssignOpBitAnd
927 | Token::AssignOpBitOr
928 | Token::AssignOpBitXor
929 | Token::AssignOpShl
930 | Token::AssignOpShr),
931 ) => parse_op_var_assign(input, e, e_start, op),
932 _ => e,
933 }
934 }
935 };
936 if input.peek_token_opt() != Some(Token::SemiColon) {
937 error_missing_semicolon(input);
938 }
939 input.next_token();
940 line_code
942}
943
944#[cold]
945#[inline(never)]
946fn error_unclosed_delimiter(
947 parser: &Parser<'_>,
948 opener_token: Token,
949 opener_span: Span,
950 expected_closer_token: Token,
951 end: Option<(Token, Span)>,
952) -> ! {
953 let message = if let Some((actual_closer_token, _)) = end {
954 format!(
955 "This {opener_token} is never closed: expected {expected_closer_token} but found {actual_closer_token}"
956 )
957 } else {
958 format!(
959 "This {opener_token} is never closed: expected {expected_closer_token} but the file ends here"
960 )
961 };
962 parser.throw_parser_err(
963 || {
964 let mut report = Report::build(
965 ariadne::ReportKind::Error,
966 (parser.ctx.src.filename.as_str(), opener_span.into()),
967 )
968 .with_message("Unclosed delimiter")
969 .with_label(
970 Label::new((parser.ctx.src.filename.as_str(), opener_span.into()))
971 .with_message(format_args!("This {opener_token} is never closed"))
972 .with_color(ariadne::Color::Red),
973 );
974
975 if let Some((actual_closer_token, actual_closer_token_span)) = end {
976 report = report.with_label(
977 Label::new((
978 parser.ctx.src.filename.as_str(),
979 actual_closer_token_span.into(),
980 ))
981 .with_message(format_args!(
982 "Expected {expected_closer_token} but found {actual_closer_token}"
983 ))
984 .with_color(ariadne::Color::Red),
985 );
986 } else {
987 report = report
988 .with_label(
989 Label::new((parser.ctx.src.filename.as_str(), parser.eof_span().into()))
990 .with_message(format_args!(
991 "Expected {expected_closer_token} but the file ends here"
992 ))
993 .with_color(ariadne::Color::Red),
994 )
995 .with_help(format_args!(
996 "Add a {} here to close it",
997 blue(expected_closer_token)
998 ));
999 }
1000
1001 report.finish()
1002 },
1003 opener_span,
1004 &message,
1005 "unclosed_delimiter",
1006 )
1007}
1008
1009#[cold]
1010#[inline(never)]
1011fn error_missing_semicolon(parser: &Parser<'_>) -> ! {
1012 let span: Span = (parser.last_token_end as u32, parser.last_token_end as u32).into();
1013 parser.throw_parser_err(
1014 || {
1015 Report::build(
1016 ariadne::ReportKind::Error,
1017 (
1018 parser.ctx.src.filename.as_str(),
1019 (parser.last_token_end..parser.last_token_end),
1020 ),
1021 )
1022 .with_message("Missing semicolon")
1023 .with_label(
1024 Label::new((
1025 parser.ctx.src.filename.as_str(),
1026 (parser.last_token_end..parser.last_token_end),
1027 ))
1028 .with_message(format_args!("Add a {} here", blue(';')))
1029 .with_color(ariadne::Color::Blue),
1030 )
1031 .with_help("All statements end with a ';'")
1032 .finish()
1033 },
1034 span,
1035 "Missing semicolon",
1036 "missing_semicolon",
1037 )
1038}
1039
1040fn parse_code(input: &mut Parser<'_>) -> Vec<Expr> {
1041 let mut output: Vec<Expr> = Vec::with_capacity(4);
1042 while let Some(e) = parse_statement(input) {
1043 output.push(e);
1044 }
1045 output
1046}
1047
1048fn parse_file_import(parser: &mut Parser<'_>) -> Expr {
1049 let (t, Span { start, end: _ }) = parser.next_token();
1050 debug_assert_eq!(t, Token::Import);
1051 let (next_token, span) = parser.next_token();
1052 let (path, is_logical, mut end) = if let Token::String(s) = next_token {
1059 let raw = parse_string(s);
1060 if raw.ends_with(".cdl") {
1061 (raw, false, span.end)
1062 } else {
1063 let last_segment = raw.rsplit(['/', '\\']).next().unwrap_or(raw.as_str());
1064 if last_segment.contains('.') {
1065 cold_path();
1066 parser.error(span, ParserErr::ImportPathBadExtension);
1067 }
1068 let mut with_ext = String::with_capacity(raw.len() + 4);
1069 with_ext.push_str(raw.as_str());
1070 with_ext.push_str(".cdl");
1071 (SmolStr::new(&with_ext), true, span.end)
1072 }
1073 } else if let Token::Identifier(first) = next_token {
1074 let mut segments = String::from(first);
1077 let mut end = span.end;
1078 while parser.peek_token_opt() == Some(Token::DoubleColon) {
1079 parser.next_token();
1080 let (seg_token, seg_span) = parser.next_token();
1081 if let Token::Identifier(seg) = seg_token {
1082 segments.push('/');
1083 segments.push_str(seg);
1084 end = seg_span.end;
1085 } else {
1086 break;
1087 }
1088 }
1089 cold_path();
1090 parser.error(
1091 (span.start, end).into(),
1092 ParserErr::LegacyNamespacedImport(segments),
1093 );
1094 } else {
1095 cold_path();
1096 parser.error(
1097 span,
1098 ParserErr::UnexpectedToken(
1099 Token::String(""),
1100 next_token,
1101 "An import is a quoted path: import \"./local.cdl\"; for a file, import \"std/string\"; for a library.",
1102 ),
1103 );
1104 };
1105 let mut binding = if parser.peek_token_opt() == Some(Token::As) {
1106 parser.next_token();
1107 let (next_token, span) = parser.next_token();
1108 let alias = if let Token::Identifier(id) = next_token {
1109 SmolStr::new(id)
1110 } else {
1111 cold_path();
1112 parser.error(
1113 span,
1114 ParserErr::UnexpectedToken(
1115 Token::Identifier(""),
1116 next_token,
1117 "Module aliases must be identifiers.",
1118 ),
1119 );
1120 };
1121 end = span.end;
1122 Some(ImportBinding::Namespace(alias))
1123 } else {
1124 None
1125 };
1126 if parser.peek_token_opt() == Some(Token::LBrace) {
1127 let brace = parser.peek_token_span();
1128 if binding.is_some() {
1129 cold_path();
1130 parser.error(brace, ParserErr::ImportAliasWithItems);
1131 }
1132 parser.next_token();
1133 let mut items: Vec<(SmolStr, Span)> = Vec::new();
1134 loop {
1135 let (token, span) = parser.next_token();
1136 match token {
1137 Token::RBrace => {
1138 end = span.end;
1139 break;
1140 }
1141 Token::Identifier(name) => {
1142 items.push((SmolStr::new(name), span));
1143 match parser.next_token() {
1144 (Token::Comma, _) => {}
1145 (Token::RBrace, span) => {
1146 end = span.end;
1147 break;
1148 }
1149 (other, span) => {
1150 cold_path();
1151 parser.error(
1152 span,
1153 ParserErr::UnexpectedToken(
1154 Token::RBrace,
1155 other,
1156 "Items in an import are names separated by commas: import \"std/assert\" { eq, ne };",
1157 ),
1158 );
1159 }
1160 }
1161 }
1162 other => {
1163 cold_path();
1164 parser.error(
1165 span,
1166 ParserErr::UnexpectedToken(
1167 Token::Identifier(""),
1168 other,
1169 "Items in an import are names separated by commas: import \"std/assert\" { eq, ne };",
1170 ),
1171 );
1172 }
1173 }
1174 }
1175 if items.is_empty() {
1176 cold_path();
1177 parser.error((brace.start, end).into(), ParserErr::ImportItemsEmpty);
1178 }
1179 binding = Some(ImportBinding::Items(items.into_boxed_slice()));
1180 }
1181 let binding = binding.unwrap_or_else(|| {
1184 let segment = module_name(&path);
1185 if !is_identifier(segment) {
1186 cold_path();
1187 parser.error(
1188 (start, end).into(),
1189 ParserErr::ImportNameNotIdentifier(segment.to_owned()),
1190 );
1191 }
1192 ImportBinding::Namespace(SmolStr::new(segment))
1193 });
1194 parser.next_token_expect(
1195 Token::SemiColon,
1196 "Import statements must end with a semicolon",
1197 );
1198 Expr::ImportFile(path, binding, is_logical, (start, end).into())
1199}
1200
1201#[must_use]
1204pub fn module_name(path: &str) -> &str {
1205 let last = path.rsplit(['/', '\\']).next().unwrap_or(path);
1206 last.strip_suffix(".cdl").unwrap_or(last)
1207}
1208
1209fn is_identifier(text: &str) -> bool {
1212 let mut lexer = Token::lexer(text);
1213 matches!(lexer.next(), Some(Ok(Token::Identifier(_)))) && lexer.next().is_none()
1214}
1215
1216fn parse_type(parser: &mut Parser<'_>) -> TypeExpr {
1217 let span = parser.peek_token_span();
1218 parser.enter(span);
1219 let t = parse_type_inner(parser);
1220 parser.leave();
1221 t
1222}
1223
1224fn parse_type_inner(parser: &mut Parser<'_>) -> TypeExpr {
1225 let t = parse_atomic_type(parser);
1226 if parser.peek_token() == Token::Pipe {
1227 let mut poly = Vec::with_capacity(2);
1228 poly.push(t);
1229 while parser.peek_token() == Token::Pipe {
1230 parser.next_token();
1231 poly.push(parse_atomic_type(parser));
1232 }
1233 TypeExpr::Union(poly.into_boxed_slice())
1234 } else {
1235 t
1236 }
1237}
1238
1239fn parse_atomic_type(parser: &mut Parser<'_>) -> TypeExpr {
1240 let (next_token, span) = parser.next_token();
1241 let mut t = if next_token == Token::Function {
1242 parser.next_token_expect(
1247 Token::LParen,
1248 "A function type needs a parameter list: fn(int, int) -> int",
1249 );
1250 let mut args: Vec<TypeExpr> = Vec::new();
1251 while parser.peek_token() != Token::RParen {
1252 args.push(parse_type(parser));
1253 if parser.peek_token() == Token::Comma {
1254 parser.next_token();
1255 } else {
1256 break;
1257 }
1258 }
1259 parser.next_token_expect(Token::RParen, "Unmatched '(' in a function type");
1260 let return_type = if parser.peek_token() == Token::Arrow {
1261 parser.next_token();
1262 Some(parse_type(parser))
1263 } else {
1264 None
1265 };
1266 TypeExpr::Fn(Box::new(FnTypeExpr {
1267 args: args.into_boxed_slice(),
1268 return_type,
1269 span: (span.start, parser.last_token_end as u32).into(),
1270 }))
1271 } else if next_token == Token::LParen {
1272 let inner = parse_type(parser);
1275 parser.next_token_expect(Token::RParen, "Unmatched '(' in a type");
1276 inner
1277 } else if next_token == Token::LBrace {
1278 let key_t = parse_type(parser);
1279 parser.next_token_expect(
1280 Token::Colon,
1281 "A colon must separate key and value types in map types",
1282 );
1283 let value_t = parse_type(parser);
1284 parser.next_token_expect(Token::RBrace, "Unmatched '{'");
1285 TypeExpr::Map(Box::new(key_t), Box::new(value_t))
1286 } else if let Token::Identifier(i) = next_token {
1287 if parser.peek_token() == Token::DoubleColon {
1288 parser.next_token();
1289 let (path, end) = parse_namespace(parser, SmolStr::new(i));
1290 if parser.peek_token() == Token::OpInf {
1295 let mut path = path.into_vec();
1296 let name = path.pop().unwrap_or_else(|| SmolStr::new(i));
1297 let args = parse_type_args(parser);
1298 TypeExpr::Generic(Box::new(GenericType {
1299 namespace: Box::from(path),
1300 name,
1301 args,
1302 span: (span.start, parser.last_token_end as u32).into(),
1303 }))
1304 } else {
1305 TypeExpr::NamespacedIdentifier(path, (span.start, end).into())
1306 }
1307 } else if parser.peek_token() == Token::OpInf {
1308 let args = parse_type_args(parser);
1312 TypeExpr::Generic(Box::new(GenericType {
1313 namespace: Box::from([]),
1314 name: SmolStr::new(i),
1315 args,
1316 span: (span.start, parser.last_token_end as u32).into(),
1317 }))
1318 } else {
1319 TypeExpr::Identifier(SmolStr::new(i), span)
1320 }
1321 } else {
1322 cold_path();
1323 parser.error(
1324 span,
1325 ParserErr::UnexpectedToken(Token::Identifier(""), next_token, "Invalid type"),
1326 );
1327 };
1328 loop {
1329 if parser.peek_token() == Token::LBracket {
1330 parser.next_token();
1331 parser.next_token_expect(Token::RBracket, "Unmatched '['");
1332 t = TypeExpr::Array(Box::new(t));
1333 } else {
1334 break;
1335 }
1336 }
1337 t
1338}
1339
1340struct TypeArgScan<'a> {
1350 input: TokenIter<'a>,
1351 pending_gt: bool,
1355}
1356
1357impl<'a> TypeArgScan<'a> {
1358 fn peek(&mut self) -> Option<Token<'a>> {
1359 if self.pending_gt {
1360 return Some(Token::OpSup);
1361 }
1362 self.input.peek().and_then(|(t, _)| t.ok())
1363 }
1364
1365 fn eat(&mut self, expected: Token<'a>) -> bool {
1366 if self.peek() == Some(expected) {
1367 if self.pending_gt {
1368 self.pending_gt = false;
1369 } else {
1370 self.input.next();
1371 }
1372 true
1373 } else {
1374 false
1375 }
1376 }
1377
1378 fn eat_gt(&mut self) -> bool {
1381 if self.eat(Token::OpSup) {
1382 return true;
1383 }
1384 if self.peek() == Some(Token::OpShr) {
1385 self.input.next();
1386 self.pending_gt = true;
1387 return true;
1388 }
1389 false
1390 }
1391
1392 fn identifier(&mut self) -> bool {
1393 if matches!(self.peek(), Some(Token::Identifier(_))) {
1394 self.input.next();
1395 true
1396 } else {
1397 false
1398 }
1399 }
1400
1401 fn args(&mut self) -> bool {
1403 if !self.eat(Token::OpInf) {
1404 return false;
1405 }
1406 loop {
1407 if !self.type_expr() {
1408 return false;
1409 }
1410 if !self.eat(Token::Comma) {
1411 return self.eat_gt();
1412 }
1413 }
1414 }
1415
1416 fn type_expr(&mut self) -> bool {
1417 loop {
1418 if !self.atomic_type() {
1419 return false;
1420 }
1421 if !self.eat(Token::Pipe) {
1422 return true;
1423 }
1424 }
1425 }
1426
1427 fn atomic_type(&mut self) -> bool {
1428 if self.eat(Token::LBrace) {
1429 if !self.type_expr() || !self.eat(Token::Colon) || !self.type_expr() {
1430 return false;
1431 }
1432 if !self.eat(Token::RBrace) {
1433 return false;
1434 }
1435 } else {
1436 if !self.identifier() {
1437 return false;
1438 }
1439 while self.eat(Token::DoubleColon) {
1440 if !self.identifier() {
1441 return false;
1442 }
1443 }
1444 if self.peek() == Some(Token::OpInf) && !self.args() {
1445 return false;
1446 }
1447 }
1448 while self.eat(Token::LBracket) {
1449 if !self.eat(Token::RBracket) {
1450 return false;
1451 }
1452 }
1453 true
1454 }
1455}
1456
1457#[derive(Clone, Copy, PartialEq, Eq)]
1462pub enum TypeArgFollow {
1463 Term,
1465 TermNoStruct,
1468 Call,
1470}
1471
1472#[inline(never)]
1477fn type_args_ahead(parser: &Parser<'_>, follow: TypeArgFollow) -> bool {
1478 debug_assert!(
1479 parser.pending_gt.is_none(),
1480 "the half of a '>>' left over from a type list is read before any expression"
1481 );
1482 let mut scan = TypeArgScan {
1483 input: parser.input.clone(),
1484 pending_gt: false,
1485 };
1486 if !scan.args() {
1487 return false;
1488 }
1489 match scan.peek() {
1490 Some(Token::LParen) => true,
1491 Some(Token::DoubleColon) => follow != TypeArgFollow::Call,
1492 Some(Token::LBrace) => follow == TypeArgFollow::Term,
1493 _ => false,
1494 }
1495}
1496
1497fn parse_type_args(parser: &mut Parser<'_>) -> Box<[TypeExpr]> {
1500 parser.next_token_expect(Token::OpInf, "A type argument list starts with '<'.");
1501 let mut args: Vec<TypeExpr> = Vec::with_capacity(2);
1502 loop {
1503 args.push(parse_type(parser));
1504 if parser.peek_token() == Token::Comma {
1505 parser.next_token();
1506 } else {
1507 break;
1508 }
1509 }
1510 parser.next_type_list_close("A type argument list ends with '>'.");
1511 Box::from(args)
1512}
1513
1514fn parse_type_params(parser: &mut Parser<'_>) -> TypeParams {
1517 if parser.peek_token_opt() != Some(Token::OpInf) {
1518 return Box::from([]);
1519 }
1520 parser.next_token();
1521 let mut params: Vec<SmolStr> = Vec::with_capacity(2);
1522 loop {
1523 let (next_token, span) = parser.next_token();
1524 if let Token::Identifier(name) = next_token {
1525 params.push(SmolStr::new(name));
1526 } else {
1527 cold_path();
1528 parser.error(
1529 span,
1530 ParserErr::UnexpectedToken(
1531 Token::Identifier(""),
1532 next_token,
1533 "Type parameters must be identifiers.",
1534 ),
1535 );
1536 }
1537 if parser.peek_token() == Token::Comma {
1538 parser.next_token();
1539 } else {
1540 break;
1541 }
1542 }
1543 parser.next_type_list_close("A type parameter list ends with '>'.");
1544 Box::from(params)
1545}
1546
1547fn parse_dylib_import(parser: &mut Parser<'_>) -> Expr {
1548 let (t, Span { start, end: _ }) = parser.next_token();
1549 debug_assert_eq!(t, Token::Dylib);
1550 let (next_token, span) = parser.next_token();
1551 let path = if let Token::String(s) = next_token {
1552 SmolStr::new(parse_string(s))
1553 } else {
1554 cold_path();
1555 parser.error(
1556 span,
1557 ParserErr::UnexpectedToken(Token::String(""), next_token, "Paths must be strings."),
1558 );
1559 };
1560 parser.next_token_expect(Token::LBrace, "Blocks need to start with '{'.");
1561 let (fn_signatures, end) = parse_fn_signature_block(parser, None);
1562 Expr::ImportDylib(path, fn_signatures, (start, end).into())
1563}
1564
1565fn parse_fn_signature_block(
1573 parser: &mut Parser<'_>,
1574 mut structs: Option<&mut Vec<Expr>>,
1575) -> (Box<[(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)]>, u32) {
1576 let mut fn_signatures: Vec<(SmolStr, Box<[TypeExpr]>, TypeExpr, Span)> = Vec::new();
1577 let end: u32;
1578 loop {
1579 if parser.peek_token() == Token::RBrace {
1580 end = parser.next_token().1.end;
1581 break;
1582 }
1583 if parser.peek_token() == Token::Struct
1584 && let Some(structs) = structs.as_deref_mut()
1585 {
1586 let declared = parse_struct_declare(parser);
1587 if let Expr::StructDeclare(_, _, span, type_params, _) = &declared
1588 && !type_params.is_empty()
1589 {
1590 cold_path();
1591 parser.error(
1592 *span,
1593 ParserErr::UnexpectedToken(
1594 Token::LBrace,
1595 Token::OpInf,
1596 "A struct a host block declares takes no type parameters.",
1597 ),
1598 );
1599 }
1600 structs.push(declared);
1601 continue;
1602 }
1603
1604 let type_start = parser.peek_token();
1605 let span = parser.peek_token_span();
1606 let first = parse_type(parser);
1607 let fn_name_span: Span;
1608 let (return_type, fn_name) = if parser.peek_token() == Token::LParen {
1609 if let TypeExpr::Identifier(name, span) = first {
1610 fn_name_span = span;
1611 (
1612 TypeExpr::Identifier(SmolStr::new_static("null"), span),
1613 name,
1614 )
1615 } else {
1616 parser.error(
1617 span,
1618 ParserErr::UnexpectedToken(
1619 Token::Identifier(""),
1620 type_start,
1621 "Function names must be identifiers.",
1622 ),
1623 );
1624 }
1625 } else {
1626 let (next_token, span) = parser.next_token();
1627 fn_name_span = span;
1628 let fn_name = if let Token::Identifier(name) = next_token {
1629 SmolStr::new(name)
1630 } else {
1631 cold_path();
1632 parser.error(
1633 span,
1634 ParserErr::UnexpectedToken(
1635 Token::Identifier(""),
1636 next_token,
1637 "Function names must be identifiers.",
1638 ),
1639 );
1640 };
1641 (first, fn_name)
1642 };
1643 parser.next_token_expect(
1644 Token::LParen,
1645 "Function arguments must be delimited by parentheses",
1646 );
1647 let mut args: Vec<TypeExpr> = Vec::with_capacity(2);
1648 loop {
1649 if parser.peek_token() == Token::RParen {
1650 break;
1651 }
1652 if parser.peek_token() == Token::Ellipsis {
1657 let span = parser.peek_token_span();
1658 parser.next_token();
1659 args.push(TypeExpr::Identifier(SmolStr::new_static("..."), span));
1660 break;
1661 }
1662 args.push(parse_type(parser));
1663 if parser.peek_token() == Token::Comma {
1664 parser.next_token();
1665 } else if !(parser.peek_token() == Token::RParen) {
1666 cold_path();
1667 let span = parser.peek_token_span();
1668 parser.error(span, ParserErr::ArgumentsMissingCommaSeparator);
1669 }
1670 }
1671 parser.next_token_expect(Token::RParen, "Unmatched ')'");
1672 parser.next_token_expect(
1673 Token::SemiColon,
1674 "Function definitions must end with a semicolon",
1675 );
1676 fn_signatures.push((fn_name, Box::from(args), return_type, fn_name_span));
1677 }
1678 (fn_signatures.into_boxed_slice(), end)
1679}
1680
1681fn parse_host_block(parser: &mut Parser<'_>) -> Expr {
1685 let (t, Span { start, end: _ }) = parser.next_token();
1686 debug_assert_eq!(t, Token::Host);
1687 let (next_token, span) = parser.next_token();
1688 let namespace = if let Token::String(s) = next_token {
1689 SmolStr::new(parse_string(s))
1690 } else {
1691 cold_path();
1692 parser.error(
1693 span,
1694 ParserErr::UnexpectedToken(
1695 Token::String(""),
1696 next_token,
1697 "Host namespaces must be strings.",
1698 ),
1699 );
1700 };
1701 parser.next_token_expect(Token::LBrace, "Blocks need to start with '{'.");
1702 let mut structs: Vec<Expr> = Vec::new();
1703 let (fn_signatures, end) = parse_fn_signature_block(parser, Some(&mut structs));
1704 Expr::HostBlock(
1705 namespace,
1706 fn_signatures,
1707 (start, end).into(),
1708 structs.into_boxed_slice(),
1709 )
1710}
1711
1712#[inline(always)]
1713fn parse_file(parser: &mut Parser<'_>, impls: &mut Vec<ImplTemplate>) -> Vec<Expr> {
1714 let mut output: Vec<Expr> = Vec::with_capacity(2);
1715 while parser.peek_token_opt().is_some() {
1717 if parser.peek_token() == Token::At {
1718 let active = parse_attributes(parser);
1719 if parser.peek_token_opt().is_none() {
1720 cold_path();
1721 let span = parser.eof_span();
1722 parser.error(span, ParserErr::AttributeWithoutItem);
1723 }
1724 if active {
1725 parse_item(parser, &mut output, impls);
1726 } else {
1727 parser.inactive += 1;
1728 parse_item(parser, &mut Vec::new(), &mut Vec::new());
1729 parser.inactive -= 1;
1730 }
1731 continue;
1732 }
1733 parse_item(parser, &mut output, impls);
1734 }
1735 output
1736}
1737
1738fn parse_item(parser: &mut Parser<'_>, output: &mut Vec<Expr>, impls: &mut Vec<ImplTemplate>) {
1740 let t = parser.peek_token();
1741 if t == Token::Impl {
1745 parse_impl_block(parser, output, impls);
1746 return;
1747 }
1748 output.push(match t {
1749 Token::Function => parse_function(parser),
1750 Token::Import => parse_file_import(parser),
1751 Token::Struct => parse_struct_declare(parser),
1752 Token::Enum => parse_enum_declare(parser),
1753 Token::Dylib => parse_dylib_import(parser),
1754 Token::Host => parse_host_block(parser),
1755 unexpected => {
1756 cold_path();
1757 let span = parser.peek_token_span();
1758 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."));
1759 }
1760 });
1761}
1762
1763fn parse_attributes(parser: &mut Parser<'_>) -> bool {
1767 let mut active = true;
1768 while parser.peek_token_opt() == Some(Token::At) {
1769 parser.next_token();
1770 let (t, span) = parser.next_token();
1771 match t {
1772 Token::Identifier("cfg") => {}
1773 Token::Identifier(name) => {
1774 cold_path();
1775 parser.error(span, ParserErr::UnknownAttribute(name));
1776 }
1777 other => {
1778 cold_path();
1779 parser.error(
1780 span,
1781 ParserErr::UnexpectedToken(
1782 Token::Identifier("cfg"),
1783 other,
1784 "An attribute is '@' followed by its name, as in @cfg(web).",
1785 ),
1786 );
1787 }
1788 }
1789 let open = parser.next_token_expect(
1790 Token::LParen,
1791 "A cfg attribute holds its condition in parentheses, as in @cfg(web).",
1792 );
1793 let holds = parse_cfg_predicate(parser);
1794 parser.next_token_expect_closer(Token::LParen, open, Token::RParen);
1795 active &= holds;
1796 }
1797 active
1798}
1799
1800fn parse_cfg_predicate(parser: &mut Parser<'_>) -> bool {
1804 const EXPECTED: &str = "a flag, key = \"value\", not(...), any(...), or all(...)";
1805 let (t, span) = parser.next_token();
1806 let Token::Identifier(name) = t else {
1807 cold_path();
1808 parser.error(
1809 span,
1810 ParserErr::UnexpectedTokenStr(EXPECTED, t, "This is the condition of a cfg attribute."),
1811 );
1812 };
1813 match parser.peek_token() {
1814 Token::LParen if matches!(name, "not" | "any" | "all") => {
1815 let (_, open) = parser.next_token();
1816 parser.enter(open);
1817 let mut results = Vec::new();
1818 while parser.peek_token() != Token::RParen {
1819 results.push(parse_cfg_predicate(parser));
1820 if parser.peek_token() == Token::Comma {
1821 parser.next_token();
1822 } else {
1823 break;
1824 }
1825 }
1826 parser.leave();
1827 parser.next_token_expect_closer(Token::LParen, open, Token::RParen);
1828 match name {
1829 "not" => {
1830 if results.len() != 1 {
1831 cold_path();
1832 parser.error(
1833 (span.start, parser.last_token_end as u32).into(),
1834 ParserErr::UnexpectedTokenStr(
1835 "exactly one condition",
1836 Token::RParen,
1837 "not(...) inverts one condition.",
1838 ),
1839 );
1840 }
1841 !results[0]
1842 }
1843 "any" => results.contains(&true),
1844 _ => !results.contains(&false),
1845 }
1846 }
1847 Token::Equals => {
1848 parser.next_token();
1849 let (value, value_span) = parser.next_token();
1850 let Token::String(value) = value else {
1851 cold_path();
1852 parser.error(
1853 value_span,
1854 ParserErr::UnexpectedToken(
1855 Token::String(""),
1856 value,
1857 "The value a cfg key is compared with is a quoted string.",
1858 ),
1859 );
1860 };
1861 parser.ctx.cfg.has_value(name, &parse_string(value))
1862 }
1863 _ => parser.ctx.cfg.is_enabled(name),
1864 }
1865}
1866
1867pub struct ParsedFile {
1872 pub code: Vec<Expr>,
1873 pub impls: Vec<ImplTemplate>,
1874}
1875
1876#[must_use]
1877pub fn parse(input: &str, src: &Source) -> ParsedFile {
1878 let mut impls: Vec<ImplTemplate> = Vec::new();
1879 let cfg = cfg::active();
1880 let code = parse_file(
1881 &mut Parser {
1882 input: Token::lexer(input).spanned().peekable(),
1883 ctx: ParserCtx { src, cfg: &cfg },
1884 last_token_end: 0,
1885 span_override: None,
1886 nesting: 0,
1887 pending_gt: None,
1888 inactive: 0,
1889 },
1890 &mut impls,
1891 );
1892 ParsedFile { code, impls }
1893}
1894
1895#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1902pub enum LineKind {
1903 Declaration,
1906 Statement,
1908 Expression,
1912}
1913
1914#[must_use]
1921pub fn classify_line(line: &str) -> LineKind {
1922 let mut tokens = Token::lexer(line);
1923 let Some(Ok(mut first)) = tokens.next() else {
1924 return LineKind::Statement;
1925 };
1926 while first == Token::At {
1929 let mut depth = 0_u32;
1930 let mut after = None;
1931 for token in tokens.by_ref() {
1932 let Ok(token) = token else {
1933 return LineKind::Statement;
1934 };
1935 match token {
1936 Token::LParen => depth += 1,
1937 Token::RParen => {
1938 depth = depth.saturating_sub(1);
1939 if depth == 0 {
1940 after = tokens.next();
1941 break;
1942 }
1943 }
1944 _ => {}
1945 }
1946 }
1947 let Some(Ok(next)) = after else {
1948 return LineKind::Statement;
1949 };
1950 first = next;
1951 }
1952 match first {
1953 Token::Function
1954 | Token::Import
1955 | Token::Struct
1956 | Token::Enum
1957 | Token::Impl
1958 | Token::Dylib
1959 | Token::Host => LineKind::Declaration,
1960 Token::Let
1963 | Token::If
1964 | Token::Else
1965 | Token::While
1966 | Token::For
1967 | Token::Match
1968 | Token::Loop
1969 | Token::Try
1970 | Token::Catch
1971 | Token::Return
1972 | Token::Throw
1973 | Token::Break
1974 | Token::Continue
1975 | Token::LBrace => LineKind::Statement,
1976 _ => {
1988 let assigns = tokens
1989 .take_while(|t| !matches!(t, Ok(Token::LParen | Token::LBrace)))
1990 .any(|t| {
1991 matches!(
1992 t,
1993 Ok(Token::Equals
1994 | Token::AssignOpAdd
1995 | Token::AssignOpSub
1996 | Token::AssignOpMul
1997 | Token::AssignOpDiv
1998 | Token::AssignOpMod
1999 | Token::AssignOpPow
2000 | Token::AssignOpBitAnd
2001 | Token::AssignOpBitOr
2002 | Token::AssignOpBitXor
2003 | Token::AssignOpShl
2004 | Token::AssignOpShr)
2005 )
2006 });
2007 if assigns {
2008 LineKind::Statement
2009 } else {
2010 LineKind::Expression
2011 }
2012 }
2013 }
2014}
2015
2016#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2018pub enum ExitLine {
2019 Bare,
2022 Call,
2025}
2026
2027#[must_use]
2036pub fn exit_line(line: &str) -> Option<ExitLine> {
2037 let mut tokens = Token::lexer(line);
2038 if !matches!(tokens.next(), Some(Ok(Token::Identifier("exit")))) {
2039 return None;
2040 }
2041 let form = match tokens.next() {
2042 None => return Some(ExitLine::Bare),
2043 Some(Ok(Token::SemiColon)) => ExitLine::Bare,
2044 Some(Ok(Token::LParen)) => {
2045 let mut depth = 1u32;
2048 loop {
2049 match tokens.next() {
2050 Some(Ok(Token::LParen)) => depth += 1,
2051 Some(Ok(Token::RParen)) => {
2052 depth -= 1;
2053 if depth == 0 {
2054 break;
2055 }
2056 }
2057 Some(Ok(_)) => {}
2058 _ => return None,
2061 }
2062 }
2063 match tokens.next() {
2064 None => return Some(ExitLine::Call),
2065 Some(Ok(Token::SemiColon)) => ExitLine::Call,
2066 _ => return None,
2067 }
2068 }
2069 _ => return None,
2070 };
2071 tokens.next().is_none().then_some(form)
2073}