mod expr;
mod items;
mod stmts;
mod types;

#[cfg(test)]
mod tests;

use crate::ast::*;
use crate::diagnostic::Diagnostic;
use crate::lexeme::Lexeme;
use crate::span::{Span, Spanned};

const MAX_NESTING_DEPTH: u32 = 256;

pub(crate) struct Parser {
    tokens: Vec<Spanned<Lexeme>>,
    pos: usize,
    diagnostics: Vec<Diagnostic>,
    depth: u32,
    /// Source bytes for newline detection (empty if unavailable).
    source: Vec<u8>,
}

impl Parser {
    pub(crate) fn new(tokens: Vec<Spanned<Lexeme>>) -> Self {
        Self {
            tokens,
            pos: 0,
            diagnostics: Vec::new(),
            depth: 0,
            source: Vec::new(),
        }
    }

    pub(crate) fn new_with_source(tokens: Vec<Spanned<Lexeme>>, source: &str) -> Self {
        Self {
            tokens,
            pos: 0,
            diagnostics: Vec::new(),
            depth: 0,
            source: source.as_bytes().to_vec(),
        }
    }

    /// Check if two spans are on the same line (no newline between them).
    /// Returns true if source is unavailable (conservative: assume same line).
    fn same_line(&self, a: Span, b: Span) -> bool {
        if self.source.is_empty() {
            return true;
        }
        let start = a.end as usize;
        let end = (b.start as usize).min(self.source.len());
        if start >= end {
            return true;
        }
        !self.source[start..end].contains(&b'\n')
    }

    pub(crate) fn parse_file(mut self) -> Result<File, Vec<Diagnostic>> {
        let file = if self.at(&Lexeme::Program) {
            self.parse_program()
        } else if self.at(&Lexeme::Module) {
            self.parse_module()
        } else {
            self.error_with_help(
                "expected 'program' or 'module' declaration at the start of file",
                "every .tri file must begin with `program <name>` or `module <name>`",
            );
            return Err(self.diagnostics);
        };

        if !self.diagnostics.is_empty() {
            return Err(self.diagnostics);
        }
        Ok(file)
    }

    fn enter_nesting(&mut self) -> bool {
        self.depth += 1;
        if self.depth > MAX_NESTING_DEPTH {
            self.error_with_help(
                "nesting depth exceeded (maximum 256 levels)",
                "simplify your program by extracting deeply nested code into functions",
            );
            return false;
        }
        true
    }

    fn exit_nesting(&mut self) {
        self.depth -= 1;
    }

    // --- Utility methods ---

    fn peek(&self) -> &Lexeme {
        &self.tokens[self.pos].node
    }

    fn current_span(&self) -> Span {
        self.tokens[self.pos].span
    }

    fn prev_span(&self) -> Span {
        if self.pos > 0 {
            self.tokens[self.pos - 1].span
        } else {
            self.current_span()
        }
    }

    fn advance(&mut self) -> &Spanned<Lexeme> {
        let tok = &self.tokens[self.pos];
        if self.pos < self.tokens.len() - 1 {
            self.pos += 1;
        }
        tok
    }

    fn at(&self, token: &Lexeme) -> bool {
        std::mem::discriminant(self.peek()) == std::mem::discriminant(token)
    }

    fn eat(&mut self, token: &Lexeme) -> bool {
        if self.at(token) {
            self.advance();
            true
        } else {
            false
        }
    }

    fn expect(&mut self, token: &Lexeme) -> Span {
        if self.at(token) {
            let span = self.current_span();
            self.advance();
            span
        } else {
            self.error_at_current(&format!(
                "expected {}, found {}",
                token.description(),
                self.peek().description()
            ));
            self.current_span()
        }
    }

    fn expect_ident(&mut self) -> Spanned<String> {
        if let Lexeme::Ident(name) = self.peek().clone() {
            let span = self.current_span();
            self.advance();
            Spanned::new(name, span)
        } else {
            self.error_at_current(&format!(
                "expected identifier, found {}",
                self.peek().description()
            ));
            Spanned::new("_error_".to_string(), self.current_span())
        }
    }

    fn try_ident(&mut self) -> Option<Spanned<String>> {
        if let Lexeme::Ident(name) = self.peek().clone() {
            let span = self.current_span();
            self.advance();
            Some(Spanned::new(name, span))
        } else {
            None
        }
    }

    fn expect_integer(&mut self) -> u64 {
        if let Lexeme::Integer(n) = self.peek() {
            let n = *n;
            self.advance();
            n
        } else {
            self.error_at_current(&format!(
                "expected integer literal, found {}",
                self.peek().description()
            ));
            0
        }
    }

    fn error_at_current(&mut self, msg: &str) {
        self.diagnostics
            .push(Diagnostic::error(msg.to_string(), self.current_span()));
    }

    fn error_with_help(&mut self, msg: &str, help: &str) {
        self.diagnostics.push(
            Diagnostic::error(msg.to_string(), self.current_span()).with_help(help.to_string()),
        );
    }

    fn parse_module_path(&mut self) -> ModulePath {
        let first = self.expect_ident();
        let mut parts = vec![first.node];
        while self.eat(&Lexeme::Dot) {
            if let Some(ident) = self.try_ident() {
                parts.push(ident.node);
            } else {
                break;
            }
        }
        ModulePath(parts)
    }
}

Dimensions

trident/src/diagnostic/mod.rs
trident/src/ir/mod.rs
trident/src/deploy/mod.rs
trident/src/syntax/mod.rs
trident/src/api/mod.rs
nebu/rs/extension/mod.rs
optica/src/render/mod.rs
trident/src/config/mod.rs
trident/src/field/mod.rs
trident/src/cli/mod.rs
optica/src/parser/mod.rs
trident/src/neural/mod.rs
trident/src/cost/mod.rs
trident/src/typecheck/mod.rs
optica/src/server/mod.rs
trident/src/package/mod.rs
optica/src/scanner/mod.rs
optica/src/output/mod.rs
trident/src/verify/mod.rs
optica/src/graph/mod.rs
trident/src/ast/mod.rs
trident/src/lsp/mod.rs
trident/src/runtime/mod.rs
trident/src/gpu/mod.rs
optica/src/query/mod.rs
trident/src/lsp/semantic/mod.rs
trident/src/verify/equiv/mod.rs
trident/src/package/hash/mod.rs
trident/src/neural/training/mod.rs
trident/src/verify/synthesize/mod.rs
trident/src/ir/tir/mod.rs
rs/macros/src/addressed/mod.rs
trident/src/package/registry/mod.rs
rs/rsc/src/lints/mod.rs
trident/src/verify/report/mod.rs
trident/src/config/resolve/mod.rs
trident/src/verify/solve/mod.rs
rs/macros/src/registers/mod.rs
trident/src/verify/smt/mod.rs
rs/macros/src/cell/mod.rs
rs/core/src/fixed_point/mod.rs
trident/src/neural/data/mod.rs
rs/core/src/bounded/mod.rs
trident/src/lsp/util/mod.rs
trident/src/typecheck/tests/mod.rs
trident/src/neural/model/mod.rs
trident/src/cost/stack_verifier/mod.rs
trident/src/syntax/grammar/mod.rs
trident/src/package/manifest/mod.rs
trident/src/ir/kir/mod.rs
trident/src/neural/inference/mod.rs
trident/src/syntax/lexer/mod.rs
trident/src/cost/model/mod.rs
trident/src/ir/lir/mod.rs
trident/src/syntax/format/mod.rs
trident/src/config/scaffold/mod.rs
trident/src/verify/sym/mod.rs
trident/src/api/tests/mod.rs
trident/src/package/store/mod.rs
trident/src/ir/tree/mod.rs
trident/src/ir/kir/lower/mod.rs
trident/src/ir/lir/lower/mod.rs
trident/src/ir/tir/lower/mod.rs
trident/src/ir/tir/builder/mod.rs
trident/src/ir/tir/neural/mod.rs
trident/src/neural/data/tir_graph/mod.rs
trident/src/syntax/parser/tests/mod.rs
cw-cyber/packages/cyber-std/src/tokenfactory/mod.rs
trident/src/ir/tree/lower/mod.rs
trident/src/ir/tir/stack/mod.rs
cw-cyber/contracts/cybernet/src/tests/mod.rs
trident/src/ir/tir/optimize/mod.rs

Local Graph