Skip to main content

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}