rl_lang/parser/parser_logic.rs
1//! Core [`Parser`] struct and its cursor primitives.
2//!
3//! Every other parser sub-module is an `impl Parser` block that depends on
4//! the methods defined here. Nothing in this file produces AST nodes directly;
5//! it only provides the machinery for navigating the token stream.
6use std::collections::HashSet;
7
8use crate::{
9 ast::{Ast, statements::Statement},
10 lexer::tokentypes::Token,
11 utils::{
12 errors::{Error, Reason},
13 source::SourceFile,
14 span::Span,
15 },
16};
17
18/// Parses a flat list of tokens and produces a [`Vec<Statement>`].
19///
20/// The parser owns the token stream and advances through it with a single
21/// `current` index cursor. All sub-parsers borrow `self` mutably and call
22/// the cursor primitives ([`peek`], [`advance`], [`match_type`], etc.) defined
23/// in this module.
24///
25/// Construct via [`Parser::parse`] - there is no public `new`.
26///
27/// [`peek`]: Parser::peek
28/// [`advance`]: Parser::advance
29/// [`match_type`]: Parser::match_type
30pub struct Parser {
31 /// the source file (text + name) carried for error reports
32 pub source_file: SourceFile,
33 /// the full token list produced by the lexer, including the terminal [`TokenType::Eof`]
34 pub tokens: Vec<Token>,
35 /// index of the token currently being examined (the "read head")
36 pub current: usize,
37 pub ast_arena: Ast,
38 /// Names of `record` types declared so far, used to disambiguate
39 /// `Name { ... }` struct literals from block bodies (e.g. `if x { }`).
40 pub record_names: std::collections::HashSet<String>,
41 /// Names of `tag` (enum) types declared so far, used to recognize
42 /// `Name.Variant` as an enum variant reference rather than a field access.
43 pub tag_names: std::collections::HashSet<String>,
44}
45
46impl Parser {
47 /// Entry point: consumes `tokens` and returns a fully-parsed statement list.
48 ///
49 /// Drives the top-level parse loop, calling [`parse_statement_to_ast`] until
50 /// [`TokenType::Eof`] is reached.
51 ///
52 /// # Errors
53 /// Returns the first [`Error`] encountered; parsing stops immediately.
54 ///
55 /// [`parse_statement_to_ast`]: Parser::parse_statement_to_ast
56 pub fn parse(
57 tokens: Vec<Token>,
58 source_file: SourceFile,
59 ) -> Result<(Ast, Vec<Statement>), Error> {
60 let mut parser = Parser {
61 source_file,
62 tokens,
63 current: 0,
64 ast_arena: Ast::new(),
65 record_names: HashSet::new(),
66 tag_names: HashSet::new(),
67 };
68
69 #[cfg(feature = "debug")]
70 log::info!("parser initialized");
71 let mut statements = Vec::new();
72
73 while !parser.is_at_end() {
74 statements.push(parser.parse_statement_to_ast()?);
75 }
76
77 #[cfg(feature = "debug")]
78 log::info!("parsing complete");
79 Ok((parser.ast_arena, statements))
80 }
81
82 /// Constructs a [`Reason::Parse`] error anchored at `span` with the source
83 /// file already attached, ready to be returned from any parse method.
84 pub fn err(&self, message: impl Into<String>, span: Span) -> Error {
85 Error::at(Reason::Parse, message, span).with_source_file(&self.source_file)
86 }
87}